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/sh
是dash
. 与许多其他 shell 一样,dash 中管道的退出值是最右侧命令的退出值。这意味着类似的事情将会返回0
(成功):
system("false | true")
Run Code Online (Sandbox Code Playgroud)
如果我使用的系统是/bin/sh
,bash
我可以通过添加选项轻松修复它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 pipefail
bash
我不知道在 Dash 中这样做,但你可以/bin/sh
完全绕过。
Perlsystem()
尝试猜测程序员想要什么,并且sh -c
仅在只有一个参数且该参数包含 shell 元字符时才使用。否则,它将直接运行给定的命令,并且您可以指定确切的参数(包括进入 shellargv[0]
或$0
shell 中的命令名称,通过“间接对象”传递)。
文档说:
请注意,参数处理因参数数量而异。如果 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", '...')
类似,但会查找$PATH
for 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 命令行的相关双引号。