Linuxではパイプラインを実行できますか?
cmd1 | cmd2
このように:
cmd2
cmd1
完全に完了するまで実行を開始しません。cmd1
エラーがある場合はcmd2
まったく実行されず、パイプラインの終了ステータスはですcmd1
。
たとえば、このパイプラインを作成する方法は次のとおりです。
false | echo ok
何も印刷せずにゼロ以外の状態を返しますか?
失敗した解決策1
set -o pipefail
パイプラインの終了状態はゼロではありませんが、cmd2
失敗しても引き続き実行されます。cmd1
解決策2失敗
cmd1 && cmd2
これはパイプラインではありません。 I/O リダイレクトがありません。
解決策3失敗
mkfifo /tmp/fifo
cmd1 > /tmp/fifo && cmd2 < /tmp/fifo
ブロックされます。
最適ではないソリューション
touch /tmp/file
cmd1 > /tmp/file && cmd2 < /tmp/file
これはうまくいくようです。しかし、ここにはいくつかの欠点があります。
I/O 速度が遅いディスクにデータを書き込みます。 (もちろん使えます一時ファイルシステムただし、これは追加のシステム要件です。)
一時ファイル名を慎重に選択する必要があります。それ以外の場合は、誤って既存のファイルを上書きする可能性があります。一時テーブル役に立つかもしれませんが、名前のないパイプは命名作業を完全に減らします。
一時ファイルを含むファイルシステムは、データ全体を保存するのに十分な大きさではない可能性があります。
一時ファイルは自動的にクリーンアップされません。
ベストアンサー1
出力サイズはわかりませんが、cmd1
パイプは知っています。バッファサイズが制限されています。。特定の量のデータがパイプに書き込まれると、誰かがパイプを読み取るまですべての後続の書き込みがブロックされます(失敗したソリューション3に似ています)。
ブロックしないことが保証されるメカニズムを使用する必要があります。非常に大きなデータの場合は、一時ファイルを使用してください。それ以外の場合、データをメモリに保存する能力がある場合(最終的にはパイプのアイデアです)、次を使用してください。
result=$(cmd1) && cmd2 < <(printf '%s' "$result")
unset result
ここの結果はcmd1
変数に保存されますresult
。cmd1
成功したらcmd2
、データを実行して提供しますresult
。最後に、result
設定を解除して関連メモリを解放してください。
注:以前はここで文字列(<<< "$result"
)を使用してデータを提供していましたが、cmd2
Stéphane Chazelasはそれがbash
望ましくない一時ファイルを生成することを観察しました。
コメントの質問に答えてください。
はい、コマンドをリンクできますランダム:
result=$(cmd1) \ && result=$(cmd2 < <(printf '%s' "$result")) \ && result=$(cmd3 < <(printf '%s' "$result")) \ ... && cmdN < <(printf '%s' "$result") unset result
いいえ、上記のソリューションは、次の理由でバイナリデータには適していません。
- コマンド置換は
$(...)
末尾の改行を食べます。 \0
コマンド置換の結果では、NUL文字()の動作は指定されていません(たとえば、Bashはこれを削除します)。
- コマンド置換は
はい、バイナリデータに関連するこれらすべての問題を回避するには、次のエンコーダ
base64
(またはuuencode
NUL文字と末尾の改行のみを処理する自家製エンコーダ)を使用できます。result=$(cmd1 > >(base64)) && cmd2 < <(printf '%s' "$result" | base64 -d) unset result
ここでは、終了値を変更せずに
>(...)
維持するためにプロセス置換()を使用する必要があります。cmd1
つまり、データがディスクに書き込まれないようにするのはかなり面倒です。中間の一時ファイルがより良いソリューションです。バラよりスティーブンの答えこれは、これに対するほとんどの懸念を解決します。