Pie*_*rre 51 shell pipe shell-script
假设我有以下管道:
cmd1 < input.txt |\
cmd2 |\
cmd4 |\
cmd5 |\
cmd6 |\
(...) |\
cmdN > result.txt
Run Code Online (Sandbox Code Playgroud)
在某些条件下,我想cmd3
在cmd2
和之间添加一个cmd4
。有没有办法创建一种条件管道而不将结果保存cmd2
到临时文件中?我会想到这样的事情:
cmd1 < input.txt |\
cmd2 |\
(${DEFINED}? cmd3 : cat ) |\
cmd4 |\
cmd5 |\
cmd6 |\
(...) |\
cmdN > result.txt
Run Code Online (Sandbox Code Playgroud)
man*_*ork 51
只是通常的&&
和||
运营商:
cmd1 < input.txt |
cmd2 |
( [ -n "$DEFINED" ] && cmd3 || cat ) |
cmd4 |
cmd5 |
cmd6 |
(...) |
cmdN > result.txt
Run Code Online (Sandbox Code Playgroud)
虽然,正如此答案所指定的那样if ... else
,如果您使用 if-else 语法,您通常会更喜欢:
...
cmd2 |
if [ -n "$DEFINED" ]; then cmd3; else cat; fi |
cmd4 |
...
Run Code Online (Sandbox Code Playgroud)
(请注意,当行以管道结尾时,不需要尾部反斜杠。)
根据Jonas 的观察更新。
如果 cmd3 可能以非零退出代码终止并且您不想cat
处理剩余的输入,请反转逻辑:
cmd1 < input.txt |
cmd2 |
( [ -z "$DEFINED" ] && cat || cmd3 ) |
cmd4 |
cmd5 |
cmd6 |
(...) |
cmdN > result.txt
Run Code Online (Sandbox Code Playgroud)
Tho*_*hor 25
if
/ else
/fi
作品。假设任何类似 Bourne 的 shell:
cmd1 < input.txt |
cmd2 |
if [ -n "$DEFINED" ]; then cmd3; else cat; fi |
cmd4 |
cmd5 |
cmd6 |
(...) |
cmdN > result.txt
Run Code Online (Sandbox Code Playgroud)
Sté*_*las 11
到目前为止给出的所有答案都替换cmd3
为cat
. 您还可以避免运行任何命令:
if [ -n "$DEFINE" ]; then
alias maybe_cmd3='cmd3 |'
else
alias maybe_cmd3=''
fi
cmd1 |
cmd2 |
maybe_cmd3
cmd4 |
... |
cmdN > result.txt
Run Code Online (Sandbox Code Playgroud)
那是 POSIX,但请注意,如果在不是-mode的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"
Run Code Online (Sandbox Code Playgroud)
或者:
eval "
cmd1 |
cmd2 |
${DEFINE:+cmd3 |}
cmd4 |
... |
cmdN > result.txt"
Run Code Online (Sandbox Code Playgroud)
在 Linux 上(至少),cat
您可以使用pv -q
whichsplice()
代替read()
+write()
在两个管道之间传递数据,从而避免数据在内核空间和用户空间之间移动两次。
作为manatwork 已接受答案的附录:请注意 and-false-or gotcha 及其与流的交互。例如,
true && false || echo foo
Run Code Online (Sandbox Code Playgroud)
输出foo
。这并不奇怪,
true && (echo foo | grep bar) || echo baz
Run Code Online (Sandbox Code Playgroud)
和
echo foo | (true && grep bar || echo baz)
Run Code Online (Sandbox Code Playgroud)
两者输出baz
。(请注意,这echo foo | grep bar
是错误的并且没有输出)。然而,
echo foo | (true && grep bar || sed -e abaz)
Run Code Online (Sandbox Code Playgroud)
什么都不输出。这可能是也可能不是您想要的。
我有一个类似的情况,我用 bash函数解决了这个问题:
if ...; then
my_cmd3() { cmd3; }
else
my_cmd3() { cat; }
if
cmd1 < input.txt |
cmd2 |
my_cmd3 |
cmd4 |
cmd5 |
cmd6 |
(...) |
cmdN > result.txt
Run Code Online (Sandbox Code Playgroud)
这是另一种可能的解决方案:
连接命名管道以创建伪管道:
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"
Run Code Online (Sandbox Code Playgroud)