実際の出力ではなく、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さらに、バイトの収集、バッファリング、送信の方法、さらにクライアント システムに接続されているnotmuchものを把握する必要があるため、複雑さが増します...ssh

関連情報