如何以POSIX方式实现'set -o pipefail' - 几乎完成,需要专家帮助

时间:2012-10-26 09:33:31

标签: linux shell command-line posix pipe

我必须以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技能来说有点过于复杂......

有人可以帮忙吗?

谢谢!

5 个答案:

答案 0 :(得分:4)

键入时:

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

您使用参数runecho调用"test";然后使用参数grep"test"|awk调用{print}。通常情况下,grep无法找到任何名为|awk{print}的文件。

要根据需要调用run,您必须像|一样逃避分号(并且您需要为&&执行类似操作或||&以及可能的命令行的其他组件;需要仔细考虑处理$(...)或反引号`...`

如果你写:

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

你至少会得到你想要的所有论据run。它是否有效是值得商榷的;我还不知道你展示的run代码是如何工作的。

[...后来...]

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

答案 1 :(得分:4)

在标准C中编写自己的shell,或修补标准shell,或者使用类似-o pipefail的现有shell。

显然-o pipefail是一个重要的功能,应该在标准的shell中,如果你问我这些黑客在标准shell中模仿它是丑陋的,甚至比无用的更糟糕。

答案 2 :(得分: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

答案 3 :(得分:-1)

我的两分钱

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

这件事似乎可以解决问题。有想法吗?

答案 4 :(得分:-2)

我想念什么吗?

my-script.sh

#!/bin/sh

set -e

cat app.log | grep 'ERROR' 

printf 'It will never print this.\n'
$ ./my-script.sh
cat: app.log: No such file or directory