条件付きパイプライン


39

次のパイプラインがあるとしましょう。

cmd1 < input.txt |\
cmd2 |\
cmd4 |\
cmd5 |\
cmd6 |\
(...) |\
cmdN > result.txt

特定の条件下で、cmd3間にcmd2and を追加したいと思いcmd4ます。cmd2の結果を一時ファイルに保存せずに、親切な条件付きパイプラインを作成する方法はありますか?私は次のようなものを考えるだろう:

cmd1 < input.txt |\
cmd2 |\
(${DEFINED}? cmd3 : cat ) |\
cmd4 |\
cmd5 |\
cmd6 |\
(...) |\
cmdN > result.txt

回答:


36

通常の&&and ||演算子:

cmd1 < input.txt |
cmd2 |
( [[ "${DEFINED}" ]] && cmd3 || cat ) |
cmd4 |
cmd5 |
cmd6 |
(...) |
cmdN > result.txt

(行がパイプで終わる場合、末尾のバックスラッシュは不要であることに注意してください。)

ジョナスの観察に従って更新します
cmd3がゼロ以外の終了コードで終了しcat、残りの入力を処理したくない場合は、ロジックを逆にします。

cmd1 < input.txt |
cmd2 |
( [[ ! "${DEFINED}" ]] && cat || cmd3 ) |
cmd4 |
cmd5 |
cmd6 |
(...) |
cmdN > result.txt

便利なヒント
反転

6
落とし穴に注意してください:unix.stackexchange.com/a/39043/19055
JonasKölker12年

2
ロジックを逆にしても効果はありません。catゼロ以外の終了ステータスで戻る場合(SIGPIPEを取得するときに一般的)、cmd3実行されます。Thorの回答または実行を回避する他のソリューションように、if / then / elseをより適切に使用しますcat
ステファンシャゼル

catは、「スルー」ストリームのようなものを接続するために使用できます
アレクサンダーミルズ

19

if/ else/ fi動作します。Bourneのようなシェルを想定:

cmd1 < input.txt |
cmd2 |
if [ -n "$DEFINED" ]; then cmd3; else cat; fi |
cmd4 |
cmd5 |
cmd6 |
(...) |
cmdN > result.txt

10

これまでに与えられたすべての答えはに置き換え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(スクリプトが始まると同じように-mode #! /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、あなたが使用することができますpv -q使用するsplice()のではなく、read()+ write()データは、カーネルとユーザー空間との間で二回移動する必要がなくなり2本のパイプ間の間でデータを渡すために。


9

マナトワークの受け入れられた答えへの補遺として: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)

何も出力しません。これは、必要な場合とそうでない場合があります。


ここでこれがどのように関連するのかわかりません。else(または||)未変更の入力を維持ヌル操作として行動しなければなりません。したがって、すべてcatecho変更に置き換えると、コードは無関係になります。:「と-偽または落とし穴」については、私は、パイプラインとの干渉見ないpastebin.com/u6Jq0bZe
manatwork

5
@manatwork:Jonasが指摘していることの1つは、in [[ "${DEFINED}" ]] && cmd3 || catcmd3およびcatが相互に排他的thenelse同じのブランチではifないcmd3が、失敗catすると実行されることです。(あればもちろん、cmd3常に成功し、またはそれは、すべての入力を消費している場合がありますので、何もして残っていないstdinためcat、それは問題ではない可能性があり、その後、プロセスに。)あなたは、両方が必要な場合thenelse枝を、それが明示的に使用することをお勧めしますif、コマンドをない&&||
musiphil

1
@musiphil、説明ありがとうございます。今、私はジョナスの議論を理解しています。
マナトワーク

4

私はbash 関数で解決した同様の状況がありました:

if ...; then
  my_cmd3() { cmd3; }
else
  my_cmd3() { cat; }
if

cmd1 < input.txt |
cmd2 |
my_cmd3 |
cmd4 |
cmd5 |
cmd6 |
(...) |
cmdN > result.txt

3

代替の可能な解決策
は次のとおりです。疑似パイプラインを作成するために名前付きパイプを連結します。

dir="$(mktemp -d)"
fifo_n='0'
function addfifo {
 stdin="$dir/$fifo_n"
((fifo_n++))
[ "$1" ] || mkfifo "$dir/$fifo_n"
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"; exit 0

2

将来の参考のために、ここに来て魚の条件付きパイプを探しているなら、これがあなたのやり方です:

set -l flags "yes"
# ... 
echo "conditionalpipeline" | \
  if contains -- "yes" $flags 
    sed "s/lp/l p/"
  else
    cat
  end | tr '[:lower:]' '[:upper:]'
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.