我使用以下行进行简单的系统调用:
system ("mkdir -p Purged") or die "Failed to mkdir." ;
执行脚本确实会进行系统调用,我可以找到一个名为Purged的目录,但仍然会打印错误消息并且脚本会死掉。我的语法出了什么问题?
答案 0 :(得分:17)
system
返回它调用的命令的退出状态。在shell中,零退出状态意味着成功。你必须颠倒逻辑:
0 == system qw(mkdir -p Purged) or die "Failed to create the dir\n";
答案 1 :(得分:9)
那会有点混乱,不是吗? - Leonardo Herrera Ikegami's answer
是的,令人困惑的是system
命令在Perl中反转了true和false,并创建了这样有趣的逻辑:
if ( system qw($command) ) {
die qq(Aw... If failed);
}
else {
say qq(Hooray! It worked!);
}
但是,system
命令执行此操作的原因是可以理解的。在Unix中,退出状态为零表示程序有效,非零状态可以为您提供system
调用失败的原因信息。也许你打电话的程序不存在。也许这个程序按预期工作。例如,当grep
有效时,1
会返回退出代码grep
,但没有匹配的行。您可能希望区分grep
何时返回零,一,或返回大于一的代码。 (是的,我知道在Perl程序中使用grep
的系统调用很愚蠢,但这是我能想到的第一个例子。)
为了防止偶然混淆,我创建了一个变量来保存system
命令的退出状态,而不是直接测试system
的输出:
my $error = system qw($command);
if ($error) {
die qq(Aw... It failed);
}
else {
say qq(Hooray! It worked!);
}
这完全没必要了,与Perl合作的人应该知道system
会颠倒Perl对 true 和 false 的定义,但如果我没有早上喝咖啡,我可能会错过它,因为我会查看其他人的代码。做这个小步骤只会使程序看起来更合乎逻辑。
system的Perldoc为您提供的代码允许您测试系统命令的输出,以确切了解发生的情况。 (如果出现错误,或系统中断信号导致system
呼叫死亡)。很高兴知道您是否需要询问system
返回值以找出问题所在。
答案 2 :(得分:6)
system
成功返回0
,因此您需要and
而不是or
。
另请参阅:use autodie qw( system );
答案 3 :(得分:2)
添加未提及但未提及的内容,可以悄悄搞砸。
system
返回'zero-or-not'只能通过shell或execvp
告诉命令本身是否成功执行; 0
不意味着该命令成功完成了它的工作。
如果返回非零,您需要解压缩$?
以获取更多信息;请参阅system了解如何执行此操作。例如,命令的实际退出代码是$? >> 8
,执行程序的设计目的是在其出口处进行通信。
总而言之,您可能希望对
的影响做些什么sub run_system {
my ($cmd, @other_agrs) = @_; # may do more via @other_args
my ($cmdref, $sys_ret) = (ref $cmd, 0); # LIST or scalar invocation?
if ($cmdref eq 'ARRAY') {
$sys_ret = system(@$cmd);
}
elsif (not $cmdref) {
$sys_ret = system($cmd);
}
else { Carp::carp "Invocation error, got $cmdref" }
return 1 if $sys_ret == 0;
# Still here? The rest is error handling.
Carp::carp "Trouble with 'system($cmd)': $?";
print "Got exit " . ($? >> 8) . " from $cmd\n";
return 0; # or die (but then design changes)
}
这可以仅仅作为system == 0 or do { ... };
或者在代码中这样做,但是这样有一个小库,我们可以更容易地改进错误处理,最终决定切换到模块来管理外部命令,此外,作为一个用户级设施现在有意义的是它在成功时返回true(我们没有改变system
的设计)。
此外,在这种情况下,mkdir -p
意味着安静,在某些情况下,它无法做任何事情。这当然是设计的,但人们应该意识到这一点。
答案 4 :(得分:0)
单行解决方案:
system("if printf '' > tmp.txt; then exit 1; else exit 0; fi ;") or die("unable to clobber tmp.txt");
答案 5 :(得分:0)
最简单
system ("mkdir -p Purged") && die "Failed to mkdir.";
答案 6 :(得分:0)
system
命令传递它运行的程序的退出代码。它不判断或解释它。 Raku, on the other hand, does interpret it 并且您必须与语言作斗争才能绕过它。而且,请记住,Perl 的 system
基本上是 C 的 system
(Perl 中的许多内容正是 C 版本所做的)。
您需要根据您对该特定命令的行为的了解,将此值与您预期的值进行比较,如 system 文档中所示:
@args = ("command", "arg1", "arg2");
system(@args) == 0
or die "system @args failed: $?"
通常,制作精良的 unix 程序使用 0 表示成功,其他所有程序都使用非零表示。这是一个很好的经验法则,但它是一个约定而不是一个要求。某些程序返回非零值以指示其他类型的成功。 system
对此一无所知。不仅如此,许多程序员不知道退出代码应该是什么,而且我经常不得不处理某人对它应该是什么的重新构思(而且我自己可能在某个时候已经这样做了)。
“信任而不验证”的趋势越来越明显,我认为这是没有系统和shell编程背景的新一代程序员。而且,即便如此,世界应该是怎样的,它实际上是怎样的。请记住,您作为程序员的目标是知道发生了什么以及为什么发生,即使世界很混乱。
这里有一些有趣的链接:
不过我最喜欢this statement from Frederick:
<块引用>更现实地说,0 表示成功或可能失败,1 表示一般失败或可能成功,如果 1 和 0 都用于成功,则 2 表示一般失败,但也可能是成功。
考虑grep
。这是要搜索的文件:
$ cat original.txt
This is the original
This is from append.txt
This is from append.txt
This is from append.txt
搜索存在的东西:
$ grep original original.txt
This is the original
退出代码为 0(shell 上的 $?
,但 Perl 的 $?
更复杂)。按照惯例,这就是您所期望的。您找到了一些东西并且没有错误:
$ echo $?
0
但是现在搜索不存在的东西。您没有找到任何内容,但没有错误:
$ grep foo original.txt
现在退出代码是 1,因为这就是 grep
所做的:
$ echo $?
1
这是非零但不是错误。您正确调用了 grep
,它产生了正确且符合预期的输出。
由 system
来判断这些退出值。它传递退出代码并让您解释它们。
现在,我们变得懒惰并假设 0 表示它有效。我们这样做是因为很多人已经习惯了看到它,而且大部分时间它都有效。但是,“如果有时间的话效果最好”与“不会咬我的屁股”不同。将 system
的结果与您认为成功的结果进行比较:
my $expected = 0;
system('...') == $expected or die ...
如果任务对成功的定义是 grep
找到匹配的行,则该代码可以正常工作,因为在这种情况下退出代码恰好为零。但是,这意外地奏效了,因为两个不同的想法恰好在同一点上对齐。
但是,您接受的成功与退出代码不同。考虑一下 grep
示例。到目前为止,您知道 0
和 1
都是正常操作。这两者都意味着 grep
没有遇到错误。这与错误地调用 grep
并获得不同的退出值不同:
$ grep -X foo original.txt
grep: original.txt: Undefined error: 0
$ echo $?
2
那个 2
通常用于表示您错误地调用了程序。例如,您使用的开关存在于一个工具的一个实现中,而在另一个工具上不存在(除了 line 和 gnu 之外还有其他东西,但较少)。
你可能会得到这样的结果:
my %acceptable = map { $_ => 1 } qw(0 1);
my $rc = system(...);
die ... unless exists $acceptable{$rc};
而且,die
可以通过该退出代码(查看它是 method for choosing a value):
$ perl -e 'system( "grep -X foo original.txt" ) == 0 or die'
grep: original.txt: Undefined error: 0
Died at -e line 1.
$ echo $?
2
当然,这不像单个声明那么漂亮,但漂亮不应该胜过正确。
但这还不够好。我的 linux 版本的 grep
说在某些情况下它会以 0
退出:
通常情况下,如果选择了一行,退出状态为 0,如果没有选择任何行,则为 1,如果发生错误,则为 2。但是,如果使用 -q 或 --quiet 或 --silent 并选择了一行,即使发生错误,退出状态也是 0。
更进一步,sysexits.h 中有一个常规退出代码列表,表示非常特殊的情况。例如,退出代码 2 获得符号 EX_USAGE
并表示命令的形成方式存在问题。
当我编写系统代码时,我非常关注正确的退出代码,因此其他程序无需解析错误输出就可以知道发生了什么。用户在 die
中没有失败的情况下调用的 system
可能会返回 255。这对于找出问题所在的其他事情不是很有用。