次から展開この問題、コマンドが成功したかどうかに応じて、コマンドの標準出力をパイプ処理するユースケースがあります。
デフォルトのパイプラインで始まります。
command | grep -P "foo"
command
しかし、時には標準出力には何も印刷されませんが、実際には終了コードがあることを確認しました。この状況を無視し、終了コードが次の場合にのみエラーコードを出力したいと0
思います。grep
1
実際の例として、次のコマンドを実装できます。
OUTPUT=$(command) # exit code is 0 or 1
RESULT=$?
if [ $RESULT -eq 0 ]; then
return $RESULT; # if the exit code is 0 then we simply pass it forward
else
grep -P "foo" <<< $OUTPUT; # otherwise check if the stdout contains "foo"
fi
しかし、ここにはスクリプトを書く必要があるという欠点がたくさんあります。つまり、コンソールでしか実行できないという意味です。これもちょっと素人的なようです。
1
よりきれいな構文のために、終了コードがある場合は終了コードを前方にパイプし、そうでなければ終了コードを前方にパイプする仮想三項演算子を想像してみてください。
command |?1 grep -P "foo" : $?
この結果を達成できる一連のオペレータとユーティリティはありますか?
ベストアンサー1
パイプラインのコマンドは同時に実行されます。これはパイプとプロセス間の通信メカニズムのすべてです。
存在する:
cmd1 | cmd2
cmd1
cmd2
同時にcmd2
記録されたデータの処理を開始しますcmd1
。
cmd2
失敗時にのみ開始する場合は、cmd1
終了ステータスを報告してcmd2
から開始する必要があるため、cmd1
パイプは使用できず、cmd1
生成されたすべてのデータを保持するために一時ファイルを使用する必要があります。
cmd1 > file || cmd2 < file; rm -f file
あるいは、あなたの例のようにメモリに保存しますが、ここには他の多くの問題があります(たとえば、末尾の改行を$(...)
すべて削除し、ほとんどのシェルは大きな出力のサイズ変更の問題は言うまでもなく、NULバイトを処理できません) 。
zsh
Linuxでは、または同じシェルを使用してbash
ここに文書を保存し、ここに文字列を一時ファイルに保存するには、次のようにします。
{ cmd1 > /dev/fd/3 || cmd2 <&3 3<&-; } 3<<< ignored
シェルに一時ファイルの作成とクリーンアップを処理させます。
bash バージョン 5 は一時ファイルを作成した後、そのファイルに対する書き込み権限を削除するため、上記の方法は機能しません。この問題を解決するには、まず書き込み権限を復元する必要があります。
{ chmod u+w /dev/fd/3
cmd1 > /dev/fd/3 || cmd2 <&3 3<&-; } 3<<< ignored
手動で、POSIX的に:
tmpfile=$(
echo 'mkstemp(template)' |
m4 -D template="${TMPDIR:-/tmp}/XXXXXX"
) && [ -n "$tmpfile" ] && (
rm -f -- "$tmpfile" || exit
cmd1 >&3 3>&- 4<&- ||
cmd2 <&4 4<&- 3>&-) 3> "$tmpfile" 4< "$tmpfile"
mktemp
一部のシステムには、一時ファイルの生成をより簡単にする非標準コマンド(システムごとに異なるインタフェースがあります)があります(tmpfile=$(mktemp)
一部のシステムではファイルを生成しないため、調整が必要な場合がありますが、ほとんどの実装では十分ですumask
)。 。これは互換性のある実装には[ -n "$tmpfile" ]
必要ありませんが、GNUは呼び出しが失敗したときにゼロ以外の終了ステータスを返さないm4
ため、少なくとも互換性がありません。m4
mkstemp()
また、コードの実行を防ぐことができるものは何もありません。快適。あなたの「スクリプト」これは、対話型シェルのプロンプトで同じ方法で入力できますが(return
コードが関数にあると仮定される部分を除く)、次のように単純化できます。
output=$(cmd) || grep foo <<< "$output"