Bashでのファイル記述子バッファリングの無効化

Bashでのファイル記述子バッファリングの無効化

ここで何が起こっているのか誤解しているかもしれませんが、これがパイプバッファリングに関連しているようです。さまざまなロギングレベルを達成するために複数のファイル記述子(#3以降)を使用するスクリプトがあります。コマンドラインオプションに応じて、それらのいくつかは同じファイルで、いくつかはコンソールで、いくつかは/dev/null。ファイルの代わりに標準出力に変換します。その理由は、複数のファイル記述子を1つのファイルにリダイレクトしたときに順序が正しくないように到着したことがわかったためです。つまり、私は喜んで

exec >/some/file 3>&1

代わりに

exec >/some/file 3>/some/file

今まではそんなに良くなった。ただし、時にはコマンドのエラー出力を取得してカスタム記述子の1つ(例:3)に送信する必要があります。この記述子はstdoutに送信できます(これはファイルに戻ります)。その後、順序が間違ったメッセージを受け取ります。このコマンドのメッセージは、後続のコマンドのメッセージの後に表示されます。これは小さなPoCです。私は何が間違っていましたか?

#!/bin/bash

check_if_ordered() {
  sort -n -k1 -k2 test_out.txt > test_out_sorted.txt
  if ! diff test_out.txt test_out_sorted.txt >/dev/null ; then
    echo "Oops, messages are NOT in order" >&2
  else
    echo "Good, messages are in order" >&2
  fi
  rm test_out.txt test_out_sorted.txt
}

log() {
  while read msg; do
    echo "$msg"
  done
}

foo() {
  for i in {1..150} ; do
    echo "$1 $i"
  done
}

#### This always works OK, but can't use it in my scenario
echo "Redirecting command output to file"
foo "1" > >(log) > test_out.txt
foo "2" > >(log) >> test_out.txt
check_if_ordered

#### This is similar to what I need to do and always fails
echo "Redirecting stdout to file"
exec >test_out.txt
foo "1" > >(log)
foo "2" > >(log)
check_if_ordered

コマンドバッファリングを無効にするための外部ツールを知っていますが、この場合は利用できないことに注意してください(私のスクリプトはできるだけ移植可能であり、さまざまなディストリビューションで実行する必要があります)。

ベストアンサー1

#### This always works OK, but can't use it in my scenario
foo "1" > >(log) > test_out.txt
foo "2" > >(log) >> test_out.txt

次のリダイレクトはstdoutをファイルに直接リダイレクトすることをオーバーライドするため、ここでプロセスのオーバーライドは重複しているように見えます。つまり、最初のものはと同じでなければなりませんfoo "1" > test_out.txt

#### This is similar to what I need to do and always fails
exec >test_out.txt
foo "1" > >(log)
foo "2" > >(log)

ここでの問題は、プロセスの交換が非同期的に実行され、ループが遅いため、2番目のループがwhile read; do echo開始されたときに最初のループがlogまだ実行中であり、パイプバッファから読み取られることです。これはecho foo > >(cat; sleep 1; echo hi)、コマンドラインで同様の操作を実行するのと似ており、次のプロンプトが表示された後に表示されますhi。また、waitここでは役に立たないようです。

logしかし、なぜ最初に2つのコピーが必要なのかわかりません。このことをする人は一人ではありません。

exec >test_out.txt
exec 9> >(log)
foo "1" >&9
foo "2" >&9

(私のシステムでは、代わりにcatそれを使用すると、より速く、より大きな塊を読むためにwhile read; do echo問題が隠されますcat。しかし、それは問題が完全に解決されるという意味ではなく、同じコピー以外の作業をしたい場合があります。)log

コマンドバッファリングを無効にできる外部ツールを知っていることに加えてください。

つまり、stdbuf -o0プロセス内部(Cライブラリ)のバッファリングにのみ役立ちます。ここではうまくいかないようです。シェルは、echo返す前に実際にオペレーティングシステムにデータを書き込む必要があります。

おすすめ記事