为单个管道命令设置管道失败


19

我需要从非BASH脚本(即PHP脚本)执行许多管道外壳命令,如下所示:

command1 | command2 | command3

因此,如果command1退出代码非零而失败,则其他命令也会失败。到目前为止,我想出的是:

set -o pipefail && command1 | command2 | command3

但是,即使它在终端上运行正常,但是如果通过脚本执行,它也会产生此结果:

sh:1:设置:非法选项-o pipefail


似乎/bin/sh不喜欢set -o pipefail。它实际上bash是伪装的,还是其他外壳?何时bash运行/bin/sh,是否接受set -o pipefail
乔纳森·勒夫勒

@JonathanLeffler我运行/bin/sh set -o pipefail时说/bin/sh: 0: Can't open set(与相同sudo)。希望我测试正确。该系统是Ubuntu12。–
Desmond Hume

你需要尝试/bin/sh -c "set -o pipefail"; 实际上,shell试图在当前目录中执行一个名为的脚本set,但找不到该脚本。
乔纳森·勒夫勒

@JonathanLeffler /bin/sh -c "set -o pipefail"无法正常工作,但是bash -c "set -o pipefail"可以正常工作。
Desmond Hume

因此,您的问题是脚本/bin/sh无法识别运行set -o pipefail。因此,您需要确保脚本运行的是/bin/bash而不是/bin/sh。或者,如果您有信心,则勇敢地(可能很笨拙)将其更改/bin/sh为链接或副本,/bin/bash而不是当前链接到的任何shell或副本。如果确定/bin/sh是这样bash,那么您使用的行为bash在以as身份运行时不会暴露/bin/sh; 使用bashbash
乔纳森·勒夫勒

Answers:


18

在Bash命令行中,您需要调用一个子shell以避免pipefail随后设置:

$ (set -o pipefail && command1 | command2 | command3)

这会将pipefail选项的作用限制在括号内创建的子外壳中(...)

一个真实的例子:

$ (set -o pipefail && false | true) && echo pipefail inactive || echo pipefail active
pipefail active

如果使用带有该-c选项的显式shell调用,则不需要子shell,该子shell具有bash或具有sh别名bash

$ bash -c "set -o pipefail && false | true" && echo pipefail inactive || echo pipefail active
pipefail active
$ sh -c "set -o pipefail && false | true" && echo pipefail inactive || echo pipefail active
pipefail active

由于您sh不接受该pipefail选项,因此我不得不假定它是-的某些旧版本或修改版本,bash或者实际上是其他外壳。


1
$在第一块命令的一部分,它只是一个shell提示符?尝试了两种方式,都没有真正起作用。该bash -c方法总是工作,不要太优雅寿..
德斯蒙德·休姆

5

不知道为什么它没有上面提到的,但也可以明确地取消设置pipefail,使用set +o pipefail

set -o pipefail
command1 | command2 | command3
set +o pipefail

如果您正在执行片段,并且不确定pipefail已经设置的片段,则可以将其与子外壳一起使用,如先前建议的那样:

# explicitly execute with pipefail set
(set -o pipefail ; command1 | command2 | command3 )
# explicitly execute with pipefail unset
(set +o pipefail ; command1 | command2 | command3 )

0

您可以结合使用$(command1)和$?:

a=$(echo "a")
[ $? -eq 0 ] && b=$(echo $a"b")
[ $? -eq 0 ] && c=$(echo $b"c")
echo $c

打印“ abc”

a=$(ls unexitingDir)
[ $? -eq 0 ] && b=$(echo $a"b")
[ $? -eq 0 ] && c=$(echo $b"c")
echo $c

打印“”。

“ [$?-eq 0] &&”表示仅在前一个命令成功时才执行以下命令。

那并不能回答您的问题,但是可以解决您的问题。


这是否意味着我将始终需要将每个命令的输出存储到变量中?
Desmond Hume 2012年

总是我不知道,但是有了我的解决方案,您将会(如果您的下一个命令需要它)。除非您能写command1 && command2 && command3
cglacet
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.