リダイレクトされたstderrはbashでバッファリングされていませんか?

リダイレクトされたstderrはbashでバッファリングされていませんか?
{
echo bla1
echo bla2 1>&2
} >>myfile 2>&1

2つのsの間に違いはありますかecho

私が考えることができる唯一の違いは、echo bla2 2>&1stderrのバッファリングされていない属性を保存することです。

どうですか?

{
echo bla1
echo bla2 1>&2 &
} >>myfile 2>&1

同じですか?

{
echo bla1
echo bla2 &
} >>myfile 2>&1

それでは、fd1とfd2は{...} >>myfile 2>&1本質的に同じになるのでしょうか、それとも過去のままになりますか?

編集する:

@ilkkachuの回答に基づいて、次のことを試しました。

perl -e 'print "foo"; sleep 2; print "bar\n"; sleep 2; print "bla"'
# ^^^line buffered
perl -e 'print "foo"; sleep 2; print "bar\n"; sleep 2; print "bla"' | cat
# ^^^block buffered
touch myfile; tail -f myfile &
perl -e 'print "foo"; sleep 2; print "bar\n"; sleep 2; print "bla"' >>myfile 2>&1
# ^^^block buffered
perl -e 'print STDERR "foo"; sleep 2; print STDERR "bar\n"; sleep 2; print STDERR "bla"'
# ^^^unbuffered
perl -e 'print STDERR "foo"; sleep 2; print STDERR "bar\n"; sleep 2; print STDERR "bla"' 2>&1 | cat
# ^^^unbuffered
perl -e 'print STDERR "foo"; sleep 2; print STDERR "bar\n"; sleep 2; print STDERR "bla"' >>myfile 2>&1
# ^^^unbuffered

fd1 の場合、バッファリングは出力タイプに応じて調整されます。

fd2の場合、出力は重要ではありません。

{ echo -n foo; sleep 2; echo bar; sleep 2; echo -n bla; }
# ^^^unbuffered
{ echo -n foo; sleep 2; echo bar; sleep 2; echo -n bla; } | cat
# ^^^unbuffered
touch myfile; tail -f myfile &
{ echo -n foo; sleep 2; echo bar; sleep 2; echo -n bla; } >>myfile 2>&1
# ^^^unbuffered
{ echo -n foo 1>&2; sleep 2; echo bar 1>&2; sleep 2; echo -n bla 1>&2; }
# ^^^unbuffered
{ echo -n foo 1>&2; sleep 2; echo bar 1>&2; sleep 2; echo -n bla 1>&2; } | cat
# ^^^unbuffered
{ echo -n foo 1>&2; sleep 2; echo bar 1>&2; sleep 2; echo -n bla 1>&2; } >>myfile 2>&1
# ^^^unbuffered

echo出力がどこに行っても常にフラッシュされるようです。

実験を繰り返す

{ printf foo; sleep 2; printf 'bar\n'; sleep 2; printf bla; }

今回も結果は同じですecho

結果: 上記の場合、バッファリングは作成者によって決定されます。

ベストアンサー1

出力バッファリングの一般的な発生は、リダイレクトやシェルに関係なく、Cランタイムライブラリのプロパティです。

プロセスが開始されると、一部のファイル記述子のみがインポートされます。その後、ランタイムライブラリは内部構造と関連バッファ(存在する場合)を構築します。興味があるのは、fd 1(stdoutの場合)が端末に接続されているかどうかです。その場合はラインバッファリングを行い、そうでない場合はブロックバッファリングを実行します。とにかくStderrはバッファリングされません。主に、パフォーマンスに関して出力を書き込むためのシステムコールは比較的高価であり、出力がファイルまたはパイプに移動する場合、待ち時間よりも帯域幅がより重要であると想定されます。

もちろん、バッファリングの動作はユーティリティによって異なり、一部のツールはCライブラリの動作自体を再実装することもできます。 (PerlやPythonのようなものがこれを行うことができると思いますが、私が知っている限り、彼らはとにかく規則に従います)--line-buffered。そしてFreeBSD grep)、自分でできないツールに強制/欺くことができるツールがあります。以下を参照してください。パイプラインでバッファリングをオフにする

それにもかかわらず、出力はバッファリングされない可能性がechoあります。外部コマンドの場合、返す前にバッファをフラッシュする必要があるという単純な理由によるものです。シェル処理自体はechoそれをバッファリングできますが(*)

Perlをテストツールとして使用するには、遅延(ブロックバッファリング)の直後にすべての出力が印刷されます。

$ perl -e '"foo \ n"印刷; sed -e 's/^/:/'
[遅延...]
:金持ち
:パブ

これにより、次の行がすぐに印刷され、その間にスリープ状態になります。

$ perl -e 'STDERR "foo\n" 印刷 2>&1 sed -e 's/^/:/'
:金持ち
[遅延...]
:パブ

(ここで重要なのは、sed出力がパイプを介して端子に行くのではなく、パイプを通過することを確認することです。)

簡単なCプログラムでも同じことができます。

あなたの質問では、リダイレクトがstdout、fd 1として印刷されるため、リダイレクトされたファイル記述子に触れないため、リダイレクトが何をecho bla2 2>&1すべきかわかりません。echo反対方向にこれを行うと、somecommand 1>&2コマンドは関連するバッファリングとともに独自の標準出力に書き込まれます。

次のようになります(今回はラインバッファリングを使用)。

$ perl -e '"foo"印刷2; "bar \ n"印刷; '
[遅延...]
プヴァ

もちろん、echoこれは終了時にバッファをフラッシュする唯一のツールではありません。バッファフラッシュをトリガする前に終了すると、他のツールと同じ結果が得られます。たとえば、両方のperl部分がラインバッファリングを使用しても、両方の部分に印刷されます。

$ perl -e '"foo"印刷'; perl -e '"bar\n" 印刷;'
金持ち[遅延...]バー

(*とkshは場合によってはバッファリングされているようです。基本的に、他のタスクがあまりない限り、1回の呼び出しなどのecho foo; echo bar;タスクwrite()を最適化します。私が持っているバージョンを除いて、遅延ループはその動作を示します。その場合、 最初にksh -c 'echo foo; i=0; while [ "$i" -lt 234567 ]; do i=$((i + 1)); done; echo bar'遅延が発生しますfoobar

おすすめ記事