次のパイプラインがあるとします。
cmd1 < input.txt |\
cmd2 |\
cmd4 |\
cmd5 |\
cmd6 |\
(...) |\
cmdN > result.txt
特定の条件下で、cmd3
とcmd2
の間にcmd4
を追加したいと思います。 cmd2
の結果を一時ファイルに保存せずに、一種の条件付きパイプラインを作成する方法はありますか?私は次のようなものを考えます:
cmd1 < input.txt |\
cmd2 |\
(${DEFINED}? cmd3 : cat ) |\
cmd4 |\
cmd5 |\
cmd6 |\
(...) |\
cmdN > result.txt
いつもの&&
および||
演算子:
cmd1 < input.txt |
cmd2 |
( [ -n "$DEFINED" ] && cmd3 || cat ) |
cmd4 |
cmd5 |
cmd6 |
(...) |
cmdN > result.txt
(行がパイプで終了する場合、末尾のバックスラッシュは必要ないことに注意してください。)
Jonasの観測 に従って更新。
cmd3がゼロ以外の終了コードで終了する可能性があり、cat
に残りの入力を処理させたくない場合は、ロジックを逆にします。
cmd1 < input.txt |
cmd2 |
( [ -z "$DEFINED" ] && cat || cmd3 ) |
cmd4 |
cmd5 |
cmd6 |
(...) |
cmdN > result.txt
if
/else
/fi
は機能します。ボーンのようなシェルを想定すると、
cmd1 < input.txt |
cmd2 |
if [ -n "$DEFINED" ]; then cmd3; else cat; fi |
cmd4 |
cmd5 |
cmd6 |
(...) |
cmdN > result.txt
これまでに出されたすべての回答は、_cmd3
_をcat
に置き換えます。また、次のコマンドを使用してコマンドを実行しないようにすることもできます。
_if [ -n "$DEFINE" ]; then
alias maybe_cmd3='cmd3 |'
else
alias maybe_cmd3=''
fi
cmd1 |
cmd2 |
maybe_cmd3
cmd4 |
... |
cmdN > result.txt
_
これはPOSIXですが、bash
スクリプトでbash
がsh
モードでない場合(_#! /path/to/bash
_で始まるスクリプトの場合など)、 _shopt -s expand_aliases
_(または_set -o posix
_)でエイリアス拡張を有効にする必要があります。
それでも不要なコマンドを実行しない別のアプローチは、evalを使用することです。
_if [ -n "$DEFINE" ]; then
maybe_cmd3='cmd3 |'
else
maybe_cmd3=''
fi
eval "
cmd1 |
cmd2 |
$maybe_cmd3
cmd4 |
... |
cmdN > result.txt"
_
または:
_eval "
cmd1 |
cmd2 |
${DEFINE:+cmd3 |}
cmd4 |
... |
cmdN > result.txt"
_
Linuxでは(少なくとも)、cat
の代わりに、splice()
+ read()
の代わりにwrite()
を使用する_pv -q
_を使用できます。 2つのパイプ間でデータを渡すため、カーネル空間とユーザー空間の間でデータが2回移動するのを防ぎます。
manatworkの承認済み回答 の補遺として、and-false-or gotchaとストリームとの相互作用に注意してください。例えば、
true && false || echo foo
foo
を出力します。驚くことではないが、
true && (echo foo | grep bar) || echo baz
そして
echo foo | (true && grep bar || echo baz)
どちらもbaz
を出力します。 (ご了承ください echo foo | grep bar
はfalseであり、出力はありません)。しかしながら、
echo foo | (true && grep bar || sed -e abaz)
何も出力しません。これは必要な場合とそうでない場合があります。
私はbash functionsで解決した同様の状況にありました:
if ...; then
my_cmd3() { cmd3; }
else
my_cmd3() { cat; }
if
cmd1 < input.txt |
cmd2 |
my_cmd3 |
cmd4 |
cmd5 |
cmd6 |
(...) |
cmdN > result.txt
以下は、可能な代替ソリューションです。
疑似パイプラインを作成するための名前付きパイプの連結:
dir="$(mktemp -d)"
addfifo() {
stdin="$dir/$fifo_n"
[ "$1" ] || mkfifo "$dir/$((fifo_n=fifo_n+1))"
stdout="$dir/$fifo_n"
}
addfifo; echo "The slow pink fox crawl under the brilliant dog" > "$stdout" &
# Restoring the famous line: "The quick brown fox jumps over the lazy dog"
# just replace 'true' with 'false' in any of the 5 following lines and the pseudo-pipeline will still work
true && { addfifo; sed 's/brilliant/lazy/' < "$stdin" > "$stdout" & }
true && { addfifo; sed 's/under/over/' < "$stdin" > "$stdout" & }
true && { addfifo; sed 's/crawl/jumps/' < "$stdin" > "$stdout" & }
true && { addfifo; sed 's/pink/brown/' < "$stdin" > "$stdout" & }
true && { addfifo; sed 's/slow/quick/' < "$stdin" > "$stdout" & }
addfifo -last; cat < "$stdin"
wait; rm -r "$dir"
今後の参考のために、ここにfishの条件付きパイプを探している場合は、次のようにします。
set -l flags "yes"
# ...
echo "conditionalpipeline" | \
if contains -- "yes" $flags
sed "s/lp/l p/"
else
cat
end | tr '[:lower:]' '[:upper:]'