是否有与 dash 等效的管道故障?

ter*_*don 7 shell scripting dash exit-status

我有一个 perl 脚本,它运行一个system()调用来执行 shell 命令,我想运行多个命令并在它们之间传输数据。类似于(在 Perl 中):

system("command1 | command2");
Run Code Online (Sandbox Code Playgroud)

Perl 的system()用途/bin/sh,并且由于它们运行在 Ubuntu Server 系统上,因此/bin/shdash. 与许多其他 shell 一样,dash 中管道的退出值是最右侧命令的退出值。这意味着类似的事情将会返回0(成功):

system("false | true")
Run Code Online (Sandbox Code Playgroud)

如果我使用的系统是/bin/shbash我可以通过添加选项轻松修复它pipefail

system("set -o pipefail; false | true");
Run Code Online (Sandbox Code Playgroud)

事实上,这在我本地的 Arch 系统上按预期工作,其/bin/sh要点是bash

$ perl -le '$status = system("false | true"); print $status>>8'
0
$ perl -le '$status = system("set -o pipefail; false | true"); print $status>>8'
1
Run Code Online (Sandbox Code Playgroud)

(是的,我知道这看起来很奇怪,但如果你不了解 Perl,请相信我的话:我们需要移动 8 才能获得实际的退出状态)

然而,由于它应该运行的机器有dash,所以同样的事情会失败:

$ perl -le '$status = system("set -o pipefail; false | true"); print $status>>8'
sh: 1: set: Illegal option -o pipefail
2
Run Code Online (Sandbox Code Playgroud)

使用set -e也不是解决方案:

$ perl -le '$status = system("set -e; false | true"); print $status>>8'
0
Run Code Online (Sandbox Code Playgroud)

我可以想到一些 Perl 解决方法,例如不使用管道或使用IPC::System::Simple模块,但我希望可能有一种更简单的方法可以直接在 dash 中执行此操作。

那么,是否有一些技巧、技巧或选项可以让我知道,如果所有dash命令都有效,则一组管道命令的退出状态应该为0(成功),如果其中任何一个命令失败,则退出状态应该为非 0 ?set -o pipefailbash

ilk*_*chu 1

我不知道在 Dash 中这样做,但你可以/bin/sh完全绕过。

Perlsystem()尝试猜测程序员想要什么,并且sh -c仅在只有一个参数且该参数包含 shell 元字符时才使用。否则,它将直接运行给定的命令,并且您可以指定确切的参数(包括进入 shellargv[0]$0shell 中的命令名称,通过“间接对象”传递)。

文档说

请注意,参数处理因参数数量而异。如果 LIST 中有多个参数,或者 LIST 是一个具有多个值的数组,则启动由列表的第一个元素给出的程序,参数由列表的其余部分给出。

如果只有一个标量参数,则检查该参数是否有 shell 元字符,如果有,则将整个参数传递到系统的命令 shell 进行解析(这是/bin/sh -c在 Unix 平台上,但在其他平台上有所不同)。

这两个会做出相同的execve()调用:

system{"/bin/sh"} ("sh", "-c", 'false |true; echo $?')
system('false |true; echo $?')
Run Code Online (Sandbox Code Playgroud)

即这个,如下所示strace

execve("/bin/sh", ["sh", "-c", "false |true; echo $?"], [/* 39 vars */]) = 0
Run Code Online (Sandbox Code Playgroud)

system("sh", "-c", '...')类似,但会查找$PATHfor sh,并且 system("/bin/sh", "-c", '...')会略有不同,因为它会传入/bin/sh启动argv[0]的 shell 而不是只是sh。没有太大区别。)

因此,我们可以在 Perl 中运行它:

system("bash", "-o", "pipefail", "-c", 'false |true; echo $?');
# or a bit shorter
system(qw/bash -o pipefail -c/, 'false |true; echo $?');
Run Code Online (Sandbox Code Playgroud)

我们避免了往返sh以及Bash 命令行的相关双引号。