我必须以POSIX的方式实现BASHset -o pipefail
选项,以便它适用于各种LINUX/UNIX风格。为了解释一下,此选项使用户能够验证所有管道命令的成功执行。启用此选项后,如果cat
失败,此命令cat app.log | grep 'ERROR'
失败,否则将抑制cat
错误。
所以,我在这里找到了一个非常好的解决方案:http://cfaj.ca/shell/cus-faq-2.html
run() {
j=1
while eval "${pipestatus_$j+:} false"; do
unset pipestatus_$j
j=$(($j+1))
done
j=1 com= k=1 l=
for a; do
if [ "x$a" = 'x|' ]; then
com="$com { $l "'3>&-
echo "pipestatus_'$j'=$?" >&3
} 4>&- |'
j=$(($j+1)) l=
else
l="$l "$$k""
fi
k=$(($k+1))
done
com="$com $l"' 3>&- >&4 4>&-
echo "pipestatus_'$j'=$?"'
exec 4>&1
eval "$(exec 3>&1; eval "$com")"
exec 4>&-
j=1
while eval "${pipestatus_$j+:} false"; do
eval "[ $pipestatus_$j -eq 0 ]" || return 1
j=$(($j+1))
done
return 0
}
上述run()
函数使用户能够以以下方式调用管道命令:
run cmd1 | cmd2 | cmd3
如果其中一个命令失败,您可以将其$?
但是,存在一个问题,它不支持管道之间的命令分组。我希望能够调用这样的东西:
run echo "test" ; grep "test" | awk '{print}'
当我这样做时,调用失败。我无法获得正确的修改来支持命令分组 - 脚本对于我的 bash 技能来说有点太复杂了......
有人可以帮忙吗?
谢谢!
我的两分钱:
#!/bin/sh
# Saving the pid of the main shell is required,
# as each element of the pipe is a subshell.
self=$$
lots_and_fail() {
seq 100
return 1
}
{ lots_and_fail || kill $self; } | sed s/7/3/
这个东西似乎可以完成这项工作。 思潮?
当您键入时:
run echo "test" ; grep "test" | awk '{print}'
你用参数echo
和"test"
调用run
;然后用参数"test"
、|
、awk
和{print}
调用grep
。 通常,grep
不会找到任何名为|
、awk
或{print}
的文件。
要根据需要调用run
,您必须像|
一样转义分号(并且您需要对命令行的&&
或||
或&
以及可能的其他组件执行类似操作;需要仔细考虑$(...)
或反引号`...`
处理)。如果你写:
run echo "test" ; grep "test" | awk '{print}'
你至少会得到你打算run
的所有论点. 它是否有效是值得商榷的;我还不明白你显示的run
代码应该如何工作。
[...后来...]
它执行一些可怕的 I/O 重定向,但将由管道符号分隔的命令的每个段包装成一个单独的象形文字小包。 它假设在参数周围用双引号括起来可以正确地中和它,这并不总是正确的(尽管很多时候都是正确的)。
你的想法的核心可能应该涉及这样的东西:
{ cmd1 ; echo $? > status1 ; } | cmd2 && grep -q '^0$' status1 }
在更长的形式中,这将是:
{ cmd1 ; echo $? > status1 ; } |
{ cmd2 ; echo $? > status2 ; } |
# ... and so on
cmdN &&
# ^ note lack of wrapper
grep -q '^0$' status1 &&
grep -q '^0$' status2 &&
# ... and so on, to N-1