我正在透過 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