我必须以POSIX方式实现BASH set -o pipefail
选项,以便它适用于各种LINUX / UNIX风格。为了解释一下,该选项使用户能够验证所有管道命令的成功执行。启用此选项后,如果cat app.log | grep 'ERROR'
失败,则此命令cat
将失败,否则将抑制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技能来说有点过于复杂......
有人可以帮忙吗?
谢谢!
键入时:
run echo "test" ; grep "test" \| awk '{print}'
你用run
和echo
参数调用"test"
;然后你用grep
,"test"
,|
和awk
参数调用{print}
。通常,grep
不会找到任何名为|
,awk
或{print}
的文件。
要根据需要调用run
,你必须像|
一样逃避分号(你需要为&&
或||
或&
以及可能的命令行的其他组件做同样的事情;处理需要仔细考虑$(...)
或反击`...`
)。
如果你写:
run echo "test" \; grep "test" \| awk '{print}'
你至少会得到你想要的所有论据run
。它是否有效是值得商榷的;我还不明白你展示的run
代码是如何工作的。
[...后来...]
它做了一些可怕的I / O重定向,但是将一个由管道符号分隔的命令的每个段包装成一个单独的小象形文字包。它假设在参数周围包装双引号可以正确地中和它,这并不总是正确的(尽管很多时候都是如此)。
在标准C中编写自己的shell,或者修补标准shell,或者使用类似-o pipefail的现有shell。
显然-o pipefail是一个重要的功能,应该在标准的shell中,如果你问我这些黑客在标准shell中模仿它是丑陋的,甚至比无用的更糟糕。
你的想法的核心应该包括这样的事情:
{ 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