次から展開この問題、コマンドが成功したかどうかに応じて、コマンドの標準出力をパイプ処理するユースケースがあります。
デフォルトのパイプラインで始まります。
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"
答え2
注:質問にタグが付けられているため強く打つ、bash機能が利用可能であると仮定します。
サンプルコードを見ると、次のようなことをしたいようです。
- コマンドの終了ステータスが 0 の場合、コマンドの終了ステータスが使用されます。
grep
それ以外の場合は、終了ステータスを使用してください。
空のパイプで実行するとgrep
コストがかからないので、とにかくパイプを使用してコマンドの終了状態を確認することをお勧めします。PIPESTATUS
配列を使用してbashから取得できます。
$ (echo foo; exit 1) | grep foo
foo
$ echo "${PIPESTATUS[@]}"
1 0
ここで、サブシェルは 1 で終了し、grep は 0 で終了します。
だからあなたはこれを行うことができます:
(command | grep -P "foo"; exit "$((PIPESTATUS[0] ? $? : 0))")
表現は単純化できますが、アイデアを得ることができます。
単に出力を偽にするオプションもあります。
(command && echo foo) | grep -P foo
echo foo
whereはコマンドが成功した場合にのみ実行されるため、grepも成功する可能性があります。