4

在以下示例echo语句中,无论管道中先前命令的退出代码如何,都会执行:

asemenov@cpp-01-ubuntu:~$ 
asemenov@cpp-01-ubuntu:~$ false|echo 123
123
asemenov@cpp-01-ubuntu:~$ true|echo 123
123
asemenov@cpp-01-ubuntu:~$ 

我希望echo命令仅在前一个命令的退出代码上执行,即我想实现这种行为:

asemenov@cpp-01-ubuntu:~$ false|echo 123
asemenov@cpp-01-ubuntu:~$ 

在bash中可能吗?

这是一个更实际的例子:

asemenov@cpp-01-ubuntu:~$ find SomeNotExistingDir|xargs ls -1
find: `SomeNotExistingDir': No such file or directory
..
..
files list from my current directory
..
..
asemenov@cpp-01-ubuntu:~$ 

xargs ls -1如果find失败则没有理由执行。

4

5 回答 5

1

管道的组件总是无条件地和逻辑地并行运行;只有当第一个(或更早的)进程成功完成时,您才能在管道中创建第二个(或以后的)进程。

在您使用 显示的特定情况下find,您至少有两个选项:

find SomeNotExistingDir ... -exec ls -1 {} +

或者您可以使用 GNU 的一个非常有用的功能( POSIXxargs中不存在):

find SomeNotExistingDir ... | xargs -r ls -1

-roption 等价于--no-run-if-emptyoption,它相当准确地解释了它的作用。如果您使用的是 GNUfind和 GNU xargs,您应该使用扩展-print0-0

find SomeNotExistingDir ... -print0 | xargs -r -0 ls -1

这会正确处理可以出现在文件名中的每个字符。

于 2012-12-10T09:17:58.260 回答
1

在命令流方面,最简单的方法是使用逻辑 OR 运算符,如下所示:

[pierrep@DEVELOPMENT8 ~]: false || echo 123
123
[pierrep@DEVELOPMENT8 ~]: true || echo 123
[pierrep@DEVELOPMENT8 ~]:

这是可行的,因为||运算符是以惰性方式计算的,这意味着仅当左语句评估为 false 或 1 时才评估右语句。

注意:成功运行的命令在成功时返回退出状态 0。当它们不是 0 时,它们不是 0。在您的示例中find

[pierrep@DEVELOPMENT8 ~]: find somedir || echo 123
find: `somedir': No such file or directory
123
[pierrep@DEVELOPMENT8 ~]: find .profile || echo 123
.profile

使用||不会重定向来自左侧命令的任何类型的输出||。如果您只想在某个命令成功时运行某个命令,您应该只进行基本的退出代码检查,并将一个命令的输出临时存储在脚本中的变量中,以便将其提供给下一个命令,如下所示:

$result=( $(find SomeNotExistingDir) )
$exit_code=$?
if [ $exit_code -eq 0 ]; then
    for path in ${result[@]}; do
        #do some stuff with the find results here...
        echo $path;
    done
fi

这是做什么的:当 find 运行时,它将其结果放入$result数组中。$?保存上次运行命令的退出代码,所以这里是find命令。如果find找到SomeNotExisitingDir,则遍历其结果(因为它可能已找到多个实例)并使用这些路径进行处理。否则什么都不做。else当执行命令时发生错误find或找不到文件/目录时, 将触发此处。

于 2012-12-10T10:01:28.373 回答
0

你不能用管道做到这一点,因为管道创建不会等待完成,否则怎么可能cat | tr 'a-z' 'A-Z'工作?使用测试和临时文件模拟管道:

file1=$(mktemp)
file2=$(mktemp)
false > $file1 && (echo 123 > $file2) < $file1 && (prog3 > $file1) < $file2 && #.... 
rm $file1 $file2
于 2012-12-10T08:11:35.123 回答
0

关键是当第一个命令失败时,第二个命令没有输出并且没有理由执行它 - 这种行为的结果变得出乎意料

如果退出代码非零时 stdout 上没有输出,则此信息本身可用于管道数据。无需检查退出代码。(当然优化部分除外。)

例如,如果你忽略优化部分,只考虑正确性部分,

find SomeNotExistingDir|xargs ls -1

可以改为

find SomeNotExistingDir| while read x; do ls -1 "$x"; done

除了while循环,里面的命令不会被执行。这种方法的缺点是,一些信息(如行号)将丢失,以便使用 awk/sed/head 等命令代替 ls。另外,在 xargs 方法的情况下, ls 将被执行 N 次,而不是 1 次。

于 2012-12-10T08:28:24.910 回答
0

对于您给出的特定示例,只需检查管道上是否有任何数据就足够了。您遇到的问题xargs是没有输入,因此它ls不带参数调用,并且ls默认打印当前目录的内容。anishsane 的解决方案几乎就足够了,但它并不完全相同,因为它会ls为每一行输出调用,这根本不是什么xargs。但是,您可以这样做:

find /bad/path | xargs sh -c 'test $# = 0 || exec ls -1 "$@"'

现在,这条管道将始终成功,也许这是不可取的(find /bad/path | xargs ls -l尽管这与你得到的行为相同)为了确保管道失败,你可以这样做:

find /bad/path | xargs sh -c 'test $# = 0 && exit 1; exec ls -1 "$@"'

然而,也有一些担忧。 xargs会很高兴地用许多参数调用它的命令(这就是它的重点!),但是一些 shell 将处理比 xargs 少得多的参数,因此 shell 很可能会截断参数。然而,这可能是一个学术问题。

于 2012-12-11T00:03:34.977 回答