2つのプログラムを1つにパイプしたいのですが、シェルがサポートしていれば、プロセス置換例えば、2つのファイルの共通行を無関係な順序でリストするには、次のようにします。
comm -12 <(sort a) <(sort b)
しかし、プロセス置換はプレーンでは存在しませんsh
。完全なPOSIX移植性を実現するために、名前付きパイプしかし、これは FIFO のディレクトリを見つけて後でクリーンアップする必要があるため面倒です。実用的な妥協案としては、2 つのシェル パイプ構造を使用し、ファイル記述子のシャッフルを使用して 1 つのパイプを別のファイル記述子に移動し、を使用して/dev/fd
パイプを指定するという方法があります。これはほとんどの Unix バリアントで機能します。
sort a | { exec 3<&0; sort b | comm -12 /dev/fd/0 /dev/fd/3; }
これは dash、bash、BusyBox sh などでは動作しますが、ksh93 および mksh では動作しません。なぜでしょうか?
$ mksh -c 'sort a | { exec 3<&0; sort b | comm -12 /dev/fd/0 /dev/fd/3; }'
$ ksh93 -c 'sort a | { exec 3<&0; sort b | comm -12 /dev/fd/0 /dev/fd/3; }'
comm: /dev/fd/0: No such device or address
答え1
他のコマンドのリダイレクトとは異なり、exec
組み込みコマンドのリダイレクトは、シェルが外部プログラムを実行するときに閉じられる場合があります。POSIXでは両方の動作が許可されている. Ksh (ATT ksh と pdksh および mksh の両方) は、外部ユーティリティを実行するときにこれらの記述子を閉じます (つまり、組み込みのリダイレクトの場合exec
、リダイレクトを実行するために呼び出した後dup2
、新しい記述子にフラグを設定しますFD_CLOEXEC
)。Bourne シェル、dash、bash、zsh、および BusyBox sh は、このリダイレクトを他のリダイレクトと同様に扱います。
2 つの入力パイプの問題に対するより移植性の高い解決策 (が存在すると仮定/dev/fd
) は、入力を読み取るコマンドで別のリダイレクトを実行し、ファイル記述子を新しいものに移動することです。この追加のリダイレクトでは、新しい記述子に close-on-exec フラグが設定されません。
sort a | { exec 3<&0; sort b | comm -12 /dev/fd/0 /dev/fd/4 4<&3; }
これは pdksh/mksh および ksh93r では動作しますが、ksh の最近のバージョン (93s+ 2008-01-31 または 93u+ 2012-08-01) では動作しません。ksh がそこで何をしているのか理解できません。