12

我必须以set -o pipefailPOSIX 方式实现 BASH 选项,以便它适用于各种 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 技能来说有点太复杂了......

有人可以帮忙吗?

谢谢!

4

3 回答 3

9

我的两分钱:

#!/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/

这件事似乎完成了这项工作。想法?

于 2019-10-02T10:15:07.580 回答
6

当您键入:

run echo "test" ; grep "test" \| awk '{print}'

run用参数调用echoand "test"; 然后你用grep参数调用"test",|和. 通常,不会找到任何名为,或.awk{print}grep|awk{print}

要根据需要调用run,您必须像您所做的那样转义分号|(并且您需要为&&or||&可能以及命令行的其他组件做类似的事情;$(...)或反引号的处理`...`需要仔细想了想)。

如果你写:

run echo "test" \; grep "test" \| awk '{print}'

你至少会得到你想要的所有论点run。那么它是否有效还有待商榷;我还不明白run您显示的代码应该如何工作。

[...之后...]

它会进行一些可怕的 I/O 重定向,但会将由管道符号分隔的命令的每一段包装成一个单独的小象形文字包。它假设在参数周围加上双引号可以正确中和它,这并不总是正确的(尽管很多时候都是正确的)。

于 2012-10-26T13:16:39.360 回答
0

你的想法的核心应该是这样的:

{ 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
于 2019-02-28T17:54:21.987 回答