Bash:匿名のfifoを作成する


30

私たちは皆知っています mkfifo そしてパイプライン。最初のものは 命名 このように、ほとんどの場合、名前を選択する必要があります。 mktemp 後でリンクを解除することを忘れないでください。もう1つは匿名のパイプを作成し、名前と削除の面倒はありませんが、パイプの両端がパイプライン内のコマンドに結び付けられているため、ファイル記述子を把握して残りの部分で使用するのはあまり便利ではありません。スクリプトの。コンパイルされたプログラムでは、私は ret=pipe(filedes); Bashには exec 5<>file だから、人は何かのように期待するだろう "exec 5<> -" または "pipe <5 >6" - Bashにそのようなものはありますか?

回答:


36

名前付きパイプを現在のプロセスに接続した直後にリンクを解除することができます。これにより、実質的に匿名パイプが生成されます。

# create a temporary named pipe
PIPE=$(mktemp -u)
mkfifo $PIPE
# attach it to file descriptor 3
exec 3<>$PIPE
# unlink the named pipe
rm $PIPE
...
# anything we write to fd 3 can be read back from it
echo 'Hello world!' >&3
head -n1 <&3
...
# close the file descriptor when we are finished (optional)
exec 3>&-

もしあなたが本当に名前付きパイプを避けたいのであれば(例えばファイルシステムが読み取り専用であるなら)、あなたの "ファイルディスクリプタのグリップを手に入れる"という考えもまたうまくいきます。 procfsを使用しているため、これはLinux固有のものです。

# start a background pipeline with two processes running forever
tail -f /dev/null | tail -f /dev/null &
# save the process ids
PID2=$!
PID1=$(jobs -p %+)
# hijack the pipe's file descriptors using procfs
exec 3>/proc/$PID1/fd/1 4</proc/$PID2/fd/0
# kill the background processes we no longer need
# (using disown suppresses the 'Terminated' message)
disown $PID2
kill $PID1 $PID2
...
# anything we write to fd 3 can be read back from fd 4
echo 'Hello world!' >&3
head -n1 <&4
...
# close the file descriptors when we are finished (optional)
exec 3>&- 4<&-

4
母、他のプロセスからのstdin / stdoutのハイジャックは総体的です。大好きです! :P
sqweek

これを未使用のファイルディスクリプタの自動検索と組み合わせることができます。 stackoverflow.com/questions/8297415/…
CMCDragonkai

22

私が知っているシェルはどれもフォークなしでパイプを作ることはできませんが、基本的なシェルパイプラインより優れているものもあります。

bash、ksh、zshでは、システムがサポートしていると仮定して /dev/fd (ほとんどの場合最近)、コマンドの入力または出力をファイル名に関連付けることができます。 <(command) からの出力に接続されているパイプを指定するファイル名に展開されます。 command、そして >(command) の入力に接続されているパイプを指定するファイル名に展開されます。 command。この機能はと呼ばれます プロセス代替 。その主な目的は、複数のコマンドを別のコマンドにパイプインしたり、別のコマンドからパイプアウトしたりすることです。

diff <(transform <file1) <(transform <file2)
tee >(transform1 >out1) >(transform2 >out2)

これは基本的なシェルパイプの欠点のいくつかを解消するのにも役立ちます。例えば、 command2 < <(command1) と同等です command1 | command2ただし、そのステータスは command2。他のユースケースは exec > >(postprocessing)これは、スクリプトの残りの部分をすべて中に入れるのと同じですが、読みやすくなります。 { ... } | postprocessing


私はdiffでこれを試してみました、そしてそれは働きました、しかし、kdiff3またはemacsで、それは働きませんでした。私の推測では、一時的な/ dev / fdファイルはkdiff3がそれを読む前に削除されています。あるいは、おそらくkdiff3がファイルを2回読み取ろうとしていて、パイプが1回しか送信していませんか
Eyal

@Eyalプロセス置換では、ファイル名はパイプへの「マジック」参照(またはこれらのマジックバリアントをサポートしていないUnixバリアント上の一時ファイル)です。マジックの実装方法はOSによって異なります。 Linuxは、それらのターゲットが有効なファイル名ではない「魔法の」シンボリックリンクとしてそれらを実装しています(それはのようなものです) pipe:[123456] ) Emacsはシンボリックリンクのターゲットは既存のファイル名ではないので、ファイルを読まないほど混乱させます(とにかく読み込むためのオプションがあるかもしれませんが、Emacsはパイプを開くのを好まないのですがとにかくファイル)。
Gilles

10

バッシュ4は持っています コプロセス

コマンドが「&amp;」制御演算子で終了した場合と同様に、サブシェルでコプロセスが非同期的に実行され、実行シェルとコプロセス間に双方向パイプが確立されます。

コプロセスの形式は次のとおりです。

coproc [NAME] command [redirections] 


@バルキ:ありがとう。私はあなたが提供したリンクで私の答えを更新しました。
Dennis Williamson

3

2012年10月現在、この機能はまだBashには存在しないようですが、名前なし/匿名パイプが必要なのは子プロセスと対話することだけであれば、coprocを使用できます。現時点でのcoprocの問題点は、明らかに一度に一つしかサポートされていないことです。なぜcoprocがこの制限を受けたのか私にはわかりません。これらは既存のタスクバックグラウンドコード(&amp; op)を拡張したものであるはずでしたが、それはbashの作者にとっては疑問です。


0

htamasからの素晴らしいそして明るい答えを使用して、私はそれをワンライナーで使用するためにそれを少し修正しました。

# create a temporary named pipe
PIPE=(`(exec 0</dev/null 1</dev/null; (( read -d \  e < /proc/self/stat ; echo $e >&2 ; exec tail -f /dev/null 2> /dev/null ) | ( read -d \  e < /proc/self/stat ; echo $e  >&2 ; exec tail -f /dev/null 2> /dev/null )) &) 2>&1 | for ((i=0; i<2; i++)); do read e; printf "$e "; done`)
# attach it to file descriptors 3 and 4
exec 3>/proc/${PIPE[0]}/fd/1 4</proc/${PIPE[1]}/fd/0
...
# kill the temporary pids
kill ${PIPE[@]}
...
# anything we write to fd 3 can be read back from fd 4
echo 'Hello world!' >&3
head -n1 <&4
...
# close the file descriptor when we are finished (optional)
exec 3>&- 4<&-

6
私はあなたのことに気づくのを助けることはできません ワンライナー 複数行あります。
Dmitry Grigoryev
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.