让 Perl 返回正确的退出代码

Posted

技术标签:

【中文标题】让 Perl 返回正确的退出代码【英文标题】:Getting Perl to return the correct exit code 【发布时间】:2011-08-16 15:04:54 【问题描述】:

我在 Ubuntu 11.04 上使用 Perl 5.10.1。我希望 Perl 执行一个 shell 脚本并使用与 shell 脚本退出相同的代码退出。但这对我不起作用...

    system($runCmd) or die("Failed to run \"$runCmd\": $!");

我已经确认单独运行“$runCmd”会返回 255 的退出代码,但不会调用“die”子句。如何使用正确的代码退出,或者至少对于不成功的代码失败?

另一个次要要求是我希望将 $runCmd 的输出打印到屏幕上。

【问题讨论】:

【参考方案1】:

正如perldoc -f die 所说,die 没有给出特定的退出代码(只有一个非零退出代码)。为了得到你想要的,你需要这样的东西:

my $exit_code=system($runCmd);

if($exit_code!=0)

  print "Command $runCmd failed with an exit code of $exit_code.\n";
  exit($exit_code >> 8);

else

  print "Command $runCmd successful!\n";

【讨论】:

system() 返回的值是一个整数,它编码子进程的退出值加上指示子进程如何退出的标志(正常终止、被信号杀死等)传递的值to exit() 只是退出值。 对,OP 希望“Perl 执行 shell 脚本并以与 shell 脚本退出相同的代码退出”,因此将 $exit_code 传递给 exit() 使程序以相同的退出退出代码作为通过将$runCmd 传递给shell 生成的退出代码。 程序 /bin/false 以退出值 1 退出。但 perl -e 'print system("/bin/false")' 打印 256。 @user5402:不一定。来自信息:“可移植程序不应假定‘false’的退出状态为 1,因为它在某些非 GNU 主机上大于 1。”我们都想要可移植的程序,不是吗? :) @musicKk 我只是指出system() 返回的值是您传递给exit() 的值的256 倍。【参考方案2】:

如果system 返回255,您需要一个and 条件。

system成功 执行时返回零。此外,die 将修改脚本的退出代码。而是 warn 并返回最后一个退出代码,如下所示:

system($cmd) and do 
    warn "Failed to run $cmd. Exit code is $?";
    exit $? >> 8;
;

为了捕捉程序的输出,请使用反引号 (`) 运算符:

my $output = `$cmd`;

if ($?) 
   warn ...;
   exit $? >> 8;

反引号运算符仅捕获STDOUT,因此对于要捕获的所有错误消息(通常转到STDERR),请修改$cmd并将2>&1附加到它。

注意$? 全局的右移八位。


致谢@musiKk:perl documentation on system() 说明如何正确检索实际退出状态。

【讨论】:

只是一个补充:你必须将system的返回值右移八位才能得到$cmd的返回值。 (perldoc.perl.org/functions/system.html)【参考方案3】:
  system($runCmd) or die("Failed to run \"$runCmd\": $!");

与大多数 Perl 函数不同,system 在成功时返回 false,在失败时返回 true。我知道这完全是倒退,但事实就是如此。

你需要“system() and”而不是“system() or”。你可能想要$? 而不是$!,尽管这有时会很棘手。

我对

有点反感
system(...)                           && die

因为它搞砸了 || die 的所有其余部分,通常会在右侧形成连续的垂直双边距,所以我有时会写

system(...) == 0                      || die

这样他们都再次正确排列。

【讨论】:

【参考方案4】:

如果 system() 看似落后的问题困扰着您,您总是可以通过以下方式使其更可口:

my $err = system '/something/that/may/not/work';
if ($err) 
    warn "@[$! || 'undefined error']\n";
    exit $err >> 8;

你的 $runCmd,顺便说一句,如果它打印到屏幕上(这是从命令行运行并且你没有重定向输出等)将打印到屏幕上。只是 IT 会将其打印到屏幕上。 Perl 不会对此负责或知道(或关心)它正在打印什么。如果这就是你想要的,并且你不想系统地分析或操纵 $runCmd 的输出,那么你就是黄金。试试看:

perl -e "system 'ls -Fahl'"

它也不会干扰你的 $runCmd 的 STDOUT。那也将进入您的终端。例如:

$ perl -e "system 'ls -Fahl /dev/null/something' and die qq(fail: $! >> 8 == @[$! >> 8])"
ls: /dev/null/something: Not a directory
fail: 26205 >> 8 == 102 at -e line 1.

【讨论】:

【参考方案5】:

我建议检查$? 变量(又名$CHILD_ERROR 是你use English; 的双关语)。这样可以更彻底地检查您的system() 调用结果。

要理解的是$? 是一个 16 位字(艺术归功于 @ikegami):

+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 15| 14| 13| 12| 11| 10|  9|  8|  7|  6|  5|  4|  3|  2|  1|  0|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+

 \-----------------------------/ \-/ \-------------------------/
            Exit code            core     Signal that killed
            (0..255)            dumped         (0..127)
                                (0..1)
system($runCmd);
if ($? == -1)  # Failed to start program / error of the wait(2) system call
    die "Failed to execute '$runCmd': $!";
 elsif ($? & 127)  # Check for non-zero signal
    die "'$runCmd' died with signal", ($? & 127), ($? & 128) ? 'with' : 'without', " coredump";
 else  # No kill signal, check exit code.
    my $exit_code = $? >> 8; # This is the number you said to be 255.
    # print "'$runCmd' exited with value ", $exit_code, "\n";

    if ($exit_code == 255) 
        die("Failed to run \"$runCmd\": $!");
     else 
        # can do additional checks for other exit codes if desired
    
    exit $exit_code;



如果你真的不想惹$?,有一个值得注意的“替代方案”。基本上,只需使用IPC::System::Simple。注意它不是核心模块,所以你需要安装它。

使用很简单:

use IPC::System::Simple qw(system);  # system() will now die properly

my $exit_value = system($runCmd);
my $exit_value2 = system($runCmd,@args);  # Alternative form that avoids shell if @args

如果您不想覆盖system,可以改用run

use IPC::System::Simple qw(run);

my $exit_value = run($runCmd);
my $exit_value2 = run($runCmd,@args);

【讨论】:

以上是关于让 Perl 返回正确的退出代码的主要内容,如果未能解决你的问题,请参考以下文章

在 Perl 中从 STDIN 捕获退出状态

Powershell 无法返回正确的退出代码

如何在 Perl 中捕获输出和退出代码时执行外部脚本?

从 Perl 线程中干净地退出

Spring Boot 应用程序失败,退出代码为 0

如何在perl脚本中忽略shell命令的退出状态