通过 ssh 执行脚本,而不是实际输出

通过 ssh 执行脚本,而不是实际输出

我正在通过 ssh 执行以下脚本:

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

问题是,有时,我得到的输出不正确,线条混杂。

在我的例子中,脚本执行了 notmuch new,正常输出如下:

...
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增加了额外的复杂程度,因为人们可能还必须弄清楚它是如何收集、缓冲和发送字节的,以及notmuchssh在客户端系统上是如何连接的……

相关内容