名前付きパイプを使用してbashパイプ「作成」関数を作成する方法

名前付きパイプを使用してbashパイプ「作成」関数を作成する方法

このページ関数を生成する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
}

おすすめ記事