res*_*es1 2 linux ssh bash shell-script
我以这种方式通过 ssh 执行脚本:
ssh user@host 'bash -s' < ./script.sh
Run Code Online (Sandbox Code Playgroud)
问题是,有时,我得到的输出不正确,行是混合的。
在我的情况下,脚本执行不是很新,正常输出是这样的:
...
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.
Run Code Online (Sandbox Code Playgroud)
但有时输出是这样的:
...
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
Run Code Online (Sandbox Code Playgroud)
并且可以肯定这不是来自 的真正输出notmuch new,该命令以 结尾,No new mail但它就像是通过 ssh 而不是逐行获取输出。
为什么会发生这种情况?
缓冲。如果我们搜索源代码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.");
$
Run Code Online (Sandbox Code Playgroud)
其中一些消息使用标准错误(默认情况下是无缓冲的),有些使用标准输出(默认情况下是行缓冲或块缓冲,取决于标准输出是发送到终端还是发送到文件)。此行为来自标准 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
$
Run Code Online (Sandbox Code Playgroud)
而相反,如果完全相同的代码被重定向到一个文件(基于块的缓冲stdout):
$ perl -E 'for (1..4) { say "out"; warn "err\n" }' >x 2>&1
$ cat x
err
err
err
err
out
out
out
out
$
Run Code Online (Sandbox Code Playgroud)
ssh增加了一层额外的复杂性,因为人们可能还必须弄清楚它是如何收集、缓冲和发送字节的,notmuch以及然后ssh连接到客户端系统上的内容......