Estoy ejecutando un script vía ssh de esta manera:
ssh user@host 'bash -s' < ./script.sh
el problema es que, a veces, el resultado que obtengo no es correcto, las líneas están mezcladas.
En mi caso, el script no se ejecuta muy nuevo, el resultado normal es algo como:
...
Note: Ignoring non-mail file: foobar
Note: Ignoring non-mail file: foobar
Note: Ignoring non-mail file: foobar
Processed 93 total files in almost no time.
No new mail.
pero a veces el resultado es algo como:
...
Note: Ignoring non-mail file: foobar
Note: Ignoring non-mail file: foobar
Processed 93 total files in almost no time.
No new mail.
Note: Ignoring non-mail file: foobar
Note: Ignoring non-mail file: foobar
y seguro que este no es el resultado real de notmuch new
, el comando termina con No new mail
pero es como si obtuviera el resultado a través de ssh, no línea por línea.
¿Por qué sucede esto?
Respuesta1
Almacenamiento en búfer. Si buscamos en el código fuentenotmuch
$ find . -name \*.c -exec grep 'Ignoring non-mail file' {} +
./notmuch-new.c: fprintf (stderr, "Note: Ignoring non-mail file: %s\n", filename);
$ find . -name \*.c -exec grep 'No new mail' {} +
./notmuch-new.c: printf ("No new mail.");
$
Algunos de esos mensajes usan error estándar (que no tiene buffer de forma predeterminada) y otros usan salida estándar (que tiene buffer de línea o de bloque de manera predeterminada, dependiendo de si la salida estándar es a una terminal o a un archivo). Este comportamiento proviene de la biblioteca C estándar, consultesetvbuf(3)
para detalles. Así, stderr
los mensajes se escriben inmediatamente, mientras que las printf
llamadas a stdout
aparecerán... bueno, depende.
El almacenamiento en búfer generalmente lo configura cada aplicación individualmente, aunque tal vez se pueda jugar con utilidades como stdbuf
(aunque algunos consideran que los LD_PRELOAD
trucos utilizados stdbuf
son bastante horribles...).
La diferencia es fácil de reproducir localmente; por ejemplo, escribiendo en la terminal (almacenamiento en búfer basado en líneas para stdout
):
$ perl -E 'for (1..4) { say "out"; warn "err\n" }'
out
err
out
err
out
err
out
err
$
mientras que, en cambio, si exactamente ese mismo código se redirige a un archivo (almacenamiento en búfer basado en bloques para stdout
):
$ perl -E 'for (1..4) { say "out"; warn "err\n" }' >x 2>&1
$ cat x
err
err
err
err
out
out
out
out
$
ssh
agrega una capa adicional de complicación, ya que es posible que también haya que descubrir cómo se recopilan, almacenan en el búfer y envían bytes, además de a notmuch
qué ssh
está conectado en el sistema cliente...