2

Я выполняю скрипт через 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 ответ1

5

Буферизация. Если мы ищем исходный код 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 подключено в клиентской системе ...

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .