リダイレクトループの作成方法

リダイレクトループの作成方法

これは主に学術的な理由によるものです。 ;-)

次のBash文を考えてみましょう。

foo | bar

これは両方のプロセスを実行しfoobar標準出力がfoo標準入力に接続されるようにしますbar。今まではそんなに良くなった。

私たちが呼び出すことができるfooメソッドはありますか?bar返品反対方向につながっていますか?

foo.stdout -> bar.stdin
foo.stdin  <- bar.stdout

私が理解したように、Bash呼び出しは相互接続されたFDペアを作成し、そのサブプロセスで/をpipe(2)置き換えるために使用できます。カーネルレベルでは2回呼び出すことができない理由はなく、上記の循環配列を配置します。stdinstdoutpipe(2)

しかし、実際にはシェル構文を理解することはできません。するそれ。

これまで私が思い出すことができる最良のアイデアは、名前付きパイプを使用するか、サブシェルを起動し、何とかクレイジーFDからFDにリダイレクトすることです。

答え1

Bashでは、次のものを使用できます。共生(bashは複数のcoprocを正しくサポートしていませんが、ここでは1つだけが必要です):

#!/bin/bash
set -e
coproc { while read -r line; do echo "$BASHPID read: $line";  done; }
i=0; while :; do
    echo "$BASHPID writing>> $i"
    echo $i >&"${COPROC[1]}"
    read -r line <&"${COPROC[0]}"
    echo "$BASHPID coproc produced>> $line"
    i=$((i+1))
done

または名前付きパイプ(単純なPOSIXシェルでも機能します):

#!/bin/bash
set -e
trap 'rm -rf "$tmpd"' EXIT
tmpd=$(mktemp -d)

mkfifo "$tmpd/p0" "$tmpd/p1"
exec 3<>"$tmpd/p0"
exec 4<>"$tmpd/p1"
rm -rf "$tmpd"

( while read -r line; do echo "$BASHPID read: $line";  done; ) <&3 >&4  &
i=0; while :; do
    echo "$BASHPID writing>> $i" 
    echo $i >&3
    read -r line <&4
    echo "$BASHPID coproc produced>> $line"
    i=$((i+1))
done 

シェルでfdを扱うことに慣れていない場合は、みんな見えないかもしれません。

また、スケジューリングへのパイプの影響により、空のパイプバッファを持つパイプから読み取るのと同じように、パイプバッファ全体を持つパイプに書き込むとブロックされるため、特定の読み書きパターンが閉じ込められます。二重ロック。

上記の2つの例の出力は次のとおりです。

32435 writing>> 0
32435 coproc produced>> 32441 read: 0
32435 writing>> 1
32435 coproc produced>> 32441 read: 1
32435 writing>> 2
32435 coproc produced>> 32441 read: 2
32435 writing>> 3
32435 coproc produced>> 32441 read: 3
32435 writing>> 4
32435 coproc produced>> 32441 read: 4

答え2

Pipes()ではこれを行うことはできないようですが、|互いに書き込んで読み取る非同期プロセスを使用するのは簡単です。

このksh93スクリプト(bash追加のダウンバージョン)2つのwhileループを開始し、その間に数字をスローし、各トランザクションの数字に1を追加します。

while read data; do
    print $(( data + 1 ))
done |&

print -p 1

while read -p data; do
    print $(( data + 1 ))
done >&p
  1. 最初のループは共同プロセスから始まります。それへの入力を待ちますread
  2. この番号は、プロセス全体を開始するために共同プロセス1に提供されます。read
  3. 2番目のループは最初のループと同じ作業を開始しますが、共同プロセスではありません。

有効な状態で実行しますxtrace(修正されたスクリプトを使用してトレースPS4ヒントを"& "最初のループに設定し、"> "2番目のループをに設定して進行状況を表示します)。

$ ksh -x script.sh
& PS4='& '
> PS4='> '
> print -p 1
> 1>& p
> read -p data
& read data
& print 2
& read data
> print 3
> read -p data
& print 4
> print 5
> read -p data
& read data
& print 6
& read data
> print 7
> read -p data
(etc.)

bashまた、共同処理(coproc組み込み検索)も実行しますが、これについてはよくわかりません。共同処理なしでシェルでこれを行うこともできます。

編集する:このようなbash

coproc while read data; do
    echo $(( data + 1 ))
done

echo 1 >&${COPROC[1]}

while read -u ${COPROC[0]} data; do
    echo $(( data + 1 ))
done >&${COPROC[1]}

実行してください(上記のように変更されたトレースプロンプトを使用して):

$ bash -x script.sh
+ PS4='& '
& PS4='> '
> echo 1
> read -u 63 data
& read data
& echo 2
> echo 3
> read -u 63 data
& read data
& echo 4
> echo 5
> read -u 63 data
& read data
& echo 6
> echo 7
> read -u 63 data
(etc.)

答え3

より複雑なファイル記述子リダイレクトツリーの場合パイプラインアクチュエータ、これDebian リポジトリ

実際、これはBASHおよびBourne様シェルの構文制限であることがよくあります。

関連情報