透過 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

相關內容