выполнение скрипта через ssh, а не реальный вывод

выполнение скрипта через ssh, а не реальный вывод

Я запускаю скрипт через ssh следующим образом:

 ssh user@host 'bash -s' < ./script.sh

проблема в том, что иногда я получаю неверный вывод, линии смешиваются.

В моем случае скрипт не выполняет ничего нового, нормальный вывод выглядит примерно так:

...
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.

но иногда вывод получается примерно таким:

...
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

и конечно же это не настоящий вывод notmuch new, команда заканчивается на , No new mailно похоже, что вывод получается через ssh, а не построчно.

Почему это происходит?

решение1

Буферизация. Если мы поищем в исходном кодеnotmuch

$ 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.");
$ 

Некоторые из этих сообщений используют стандартную ошибку (которая по умолчанию не буферизуется), а некоторые используют стандартный вывод (который по умолчанию буферизуется строками или блоками, в зависимости от того, идет ли стандартный вывод на терминал или в файл). Такое поведение исходит из стандартной библиотеки C, см.setvbuf(3)для подробностей. Таким образом, stderrсообщения пишутся немедленно, а printfвызовы stdoutбудут отображаться... ну, это как.

Буферизация обычно настраивается каждым приложением индивидуально, хотя ее можно обойти с помощью таких утилит, как stdbuf(хотя некоторые считают LD_PRELOADтрюки, используемые , stdbufдовольно ужасными...).

Разницу легко воспроизвести локально, например, записав на терминал (построчная буферизация для stdout):

$ perl -E 'for (1..4) { say "out"; warn "err\n" }'
out
err
out
err
out
err
out
err
$ 

в то время как вместо этого, если этот же самый код перенаправить в файл (буферизация на основе блоков для 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добавляет дополнительный уровень сложности, поскольку, возможно, придется также выяснить, как он собирает, буферизует и отправляет байты, а также notmuchто, sshк чему он подключен в клиентской системе...

Связанный контент