パイプ生産者は、パイプ消費者に「ファイルの終わり」に達したことをどのように伝えますか? (名前のないパイプ、名前のないパイプ)

パイプ生産者は、パイプ消費者に「ファイルの終わり」に達したことをどのように伝えますか? (名前のないパイプ、名前のないパイプ)

必要なアプリケーションがあります。生産者ファイル名の送信消費者、そしてあります生産者指示する消費者最後のファイル名が転送されたときファイルの終わり達成。

簡略化のため、次の例では生産者 デモンストレーションにはとを使用しechoprintfデモには消費者を使用しますcat。私は「ここにファイルを保存する」方法を推論しようとしましたが、成功しませんでした<<EOFプロデューサーラッパー(そのようなものが存在する場合)ファイルの終わり。有効な場合は、出力からフィルタリングcatする必要があります。EOF

例1)

入力する

{
echo "Hello World!" 
printf '\x04' 
echo "EOF"
} <<EOF |\
cat

出力

bash: warning: here-document at line 146 delimited by end-of-file (wanted `EOF')
Hello World!
EOF

例2)

入力する

{ 
echo "Hello World!" 
printf '\x04' 
echo "EOF"
} |\
cat <<EOF

出力

bash: warning: here-document at line 153 delimited by end-of-file (wanted `EOF')

区切り文字を表示するための「ここにファイルを保存する」方法は、静的テキストでのみ機能し、動的に生成されたテキストでは機能しないことは正しいですか?

--実用的なアプリケーション--

inotifywait -m --format '%w%f' /Dir |  <consumer>

コンシューマは /Dir ディレクトリにファイルが書き込まれるのを待っています。 「/Dir/EOF」ファイルが作成されると、コンシューマは次のようにシェルスクリプトを作成して論理ファイルの終了条件を検出できます。

inotifywait -m --format '%w%f' /Dir |<</Dir/EOF  <consumer>

——ザイルズの回答に対する回答——

理論的に可能ですか?

cat <<EOF
hello
world
EOF

〜のように

SpecialSymbol="EOF"
{
    echo hello
    echo world
    echo $SpecialSymbol
} |\
while read Line; do 
  if [[ $Line == $SpecialSymbol ]]
    break
  else 
    echo $Line
  fi
done |\
cat

理論的には、たぶん私の言葉は「既存の使用パターンをサポートし、以前に違法な構文を持つ追加使用パターンのみを有効にしますか?」- 既存の法律および規制が損なわれないことを意味します。

答え1

パイプの場合、すべてのプロデューサがパイプのファイル記述子を閉じ、消費者がすべてのデータを読み取った後、消費者はファイルの終わりを表示します。

したがって:

{
  echo foo
  echo bar
} | cat

catecho2番目のエントリが終了し、合計がcat読み込まれると、ファイルの終わりが表示されます。もうやることはありません。foo\nbar\n

ただし、覚えておくべきことは、パイプの左側のコマンドの一部がバックグラウンドプロセスを開始すると、そのバックグラウンドプロセスがパイプのfd(stdout)を継承するため、そのcatプロセスも終了するまでeofまたは終了が表示されないことです。標準出力。良い:

{
  echo foo
  sleep 10 &
  echo bar
} | cat

cat返品なしで10秒経過したことがわかります。

sleepここでstdoutを別のものにリダイレクトできます。たとえば、/dev/null対応する(非)出力が次に供給されることを望まない場合cat

{
  echo foo
  sleep 10 > /dev/null &
  echo bar
} | cat

左側のサブシェルで最後のコマンドを実行する前にパイプの書き込み端を閉じるには、stdoutを|閉じるか、中央のサブシェルにリダイレクトを使用できますexec。たとえば、次のようになります。

{
  echo foo
  exec > /dev/null
  sleep 10
} | (cat; echo "cat is now gone")

ただし、ほとんどのシェルはコマンドに加えてこのサブシェルを待ちますcat。したがって、読んだcat is now gone直後に表示できますが、fooパイプライン全体が完了するまで10秒待つ必要があります。もちろん、上記の例では、次のように書く方が合理的です。

echo foo | cat
sleep 10

<<ANYTHING...content...ANYTHINGコマンドの標準入力を以下を含むファイルにするここのドキュメントです。コンテンツ。そこには役に立たない。\4は、端末から読み取られたときに端末装置に格納されているデータがデータを読み取るアプリケーションにフラッシュされるバイトです(データがない場合はread()0が返され、ファイルの終わりを示します)。繰り返しますが、ここでは役に立ちません。

答え2

あなたの試みの問題は、あなたが「ここにファイルを保存する方法」と呼ぶものが存在しないことです。ここにあるファイルはシェルプログラミング言語の構文機能です。たとえば、に置き換えられませんcat

ここにドキュメントを含むシェルスクリプトを考えてみましょう。

cat <<EOF
hello
world
EOF

どのように機能するかは、スクリプトの解析中にシェルインタプリタがhereドキュメントの構文構造を見ることです。これらのコード行を実行すると、インタプリタはデータを収集してcatシェルに従ってinputに渡します。

echo 'hello
world
' >/tmp/heredoc.tmp
cat </tmp/heredoc.tmp
rm /tmp/heredoc.tmp

(ただし、一時ファイルのより賢明な管理)または

echo 'hello
world
' | cat

echo EOFここで文書の終わりを示す場合は、コマンドの出力はechoシェルでなければなりません。

{
  echo 'cat <<EOF'
  echo hello
  echo world
  echo EOF
} | sh

これはうまくいきますが、ほとんど役に立ちません。

これらのどれもあなたが計画したことを達成するのを助けません。ここにあるドキュメントには、他の入力よりも終わりが「より難しく」する魔法のようなものはありません。ファイルの終わりは、入力チャンネルを介して送信されるマーカーではなく、入力チャンネルの条件です。入力が通常のファイルの場合、アプリケーションがファイルの最後のバイトを読み取ろうとすると、ファイルの終了条件がトリガーされます。入力がパイプ(名前が指定または指定されていない)の場合、アプリケーションは読み取りを試みますが、プロデューサーがパイプを閉じるとファイル終了条件がトリガーされます。

普通は書くだけで十分です。

producer | consumer

終了すると、producerパイプの書き込み終了は開いているプロセスがないため閉じられます。パイプを早く閉じるには、生産者が標準出力(exec >&-sh、close(1)C)を閉じる準備をします。

パイプのファイルの終わりに達するには、パイプの書き込み側を開いたすべてのプロセスがパイプを閉じる必要があります。プロデューサがバックグラウンドプロセスを実行している場合は、fcntl(1, F_SETFD, fcntl(1, F_GETFC, 0) | FD_CLOEXEC)パイプ()をフォークする前に実行時に閉じるフラグを設定する必要があります。

答え3

FIFOを使用したい場合は、もちろん次のようにすることができます。

スクリプト1

FIFO_PATH="/path/to/fifo"
exec 3<"$FIFO_PATH" # stalls until FIFO is opened for writing, too
while read line; do
    : whatever
done <&3
exec 3<&-
: continue script

スクリプト2

FIFO_PATH="/path/to/fifo"
exec 3>"$FIFO_PATH" # stalls until FIFO is opened for reading, too
echo foo >&4
exec 4>&-
: continue script

関連情報