このページ関数を生成するcompose
ための擬似コードについて詳しく説明します。Nコマンドを発行してパイプラインで実行します。
compose
以下のコマンドを作成します。compose cmd1 cmd2 ... cmdn
これはシェルコマンドのように機能します。
cmd1 | cmd2 | ... | cmdn
頑張っています名前付きパイプ私はcompose
実際にBashで書くことに興味があります。残念ながら、これを行うと、出力はまったく出ません。それはおそらく、他のパイプに対する読み書きの競合状態によって引き起こされたようです。何度も繰り返したが、混乱し続ける動作が続く。私はそれをこの小さな問題に縮小しました。
echo foo | # stdin
{
mkfifo p # create pipe p
cat > p & # direct stdin to pipe p
cat < p # read pipe p to stdout
rm p # remove pipe p
}
この出力が欲しいfoo
が、何も得られない。私は何が間違っていましたか?
答え1
質問のサンプルコードの問題は微妙です。名前付きパイプに書き込むには、&
コマンドをバックグラウンドに置く必要があります。それ以外の場合は、読み取りを待つのがブロックされます。ところで、こうすれば」バックグラウンドで開始されたコマンドは、&
標準入力が[to]にリダイレクトされます/dev/null
。「は、標準入力ではなく/dev/null
パイプを介して入力された内容を意味します。p
Bashでは、ソリューションは簡単です。 stdinをバックグラウンドプロセスにリダイレクトします0<&0
。これで例は正しく機能します。
$ echo foo | { mkfifo p; cat 0<&0 > p & cat < p; rm p; }
foo
完全なcompose
関数は次のように表示されます。
compose() {
dir=$(mktemp -d) # Create a temp dir to hold the pipes
cd $dir # to avoid filename conflicts
i=0 #
mkfifo "pipe$i" # Create pipe0, the output for command $1
($1 0<&0 > "pipe$i" &) # Start $1, reading stdin and writing to pipe0
shift # Shift off $1 since it's running
for c in "$@"; do # Loop over the remaining commands
ii=$((i+1)) #
mkfifo "pipe$ii" # Create a pipe i+1, the next command's output
($c < "pipe$i" > "pipe$ii" &) # Start the next command, reading from the
i=$ii # i'th pipe and writing to the i+1'th
done #
cat "pipe$i" # Output the last pipe, executing the commands
cd - > /dev/null # Change back to the old directory
rm -rf $dir # Remove all the pipes
}
答え2
compose()
case $# in
[01]) "$@" ;; ## if 1 or fewer args just run what we've got
*) "$1" | { ## otherwise pipe output from $1 into a
shift; compose "$@" ## self-call until all args are gone
}; esac