Perl:从'system'命令捕获正确的返回值

时间:2012-05-04 22:48:06

标签: perl exit-code

我是Perl的初学者。我有一个Windows批处理脚本,其中包含多个NMake命令。此批处理脚本的一个现有问题是,即使NMake命令在执行期间失败,ERRORLEVEL也无法正确设置。

因此,在解析日志文件之前,我们永远不知道该命令是否有效。我调查了它但找不到解决方案。我,然后考虑将这个批处理脚本转换为Perl脚本,假设陷阱错误会更容易但看起来并不那么容易:)

每当我运行我的Perl脚本时,'system'命令总是返回0.我查看了许多不同的链接,并意识到捕获'system'命令的正确返回状态并不是那么简单。不过,我尝试了这些建议,但事情并没有奏效。 :(

让我提一下,在执行过程中调用的NMake命令会调用许多不同的命令。例如,下面提到的命令输出,即抛出“致命错误”,实际上是Perl脚本(check_dir.pl)的一部分。对Perl脚本的调用是在NMake文件中编写的。

如果我直接调用此Perl文件(check_dir.pl并检查退出值,我会得到正确的结果,即命令失败并打印非零退出值(... 意外返回退出值2 )。

尝试过Perl的系统功能,但它没有帮助。我使用了以下代码:

system ("nmake /f _nt.mak pack_cd SUB_PLAT=$PLAT DR=$plat 2>&1");

if ( $? == -1 ) {
    print "Command failed to execute: $!\n";
}
elsif ( $? & 127 ) {
    printf "The child died with signal %d, %s a coredump\n",
    ( $? & 127 ), ( $? & 128 ) ? 'with' : 'without';
}
else {
    printf "child exited with value %d\n", $? >> 8;
}

输出:

.....  
.....  
Unable to open dir: R:\TSM_Latest  
Compressing...NMAKE : fatal error U1077: 'if' : return code '0x2'  
Stop.  
child exited with value 0

也尝试过:

use IPC::System::Simple qw(system);  
my $exit_status = system ("nmake /f _nt.mak pack_cd SUB_PLAT=$PLAT DR=$plat 2>&1");

if ($exit_status != 0) {  
    print "Failure";  
    exit 3;  
} else {  
    print "Success";  
}

最后尝试了以下模块:

use IPC::Run qw( run timeout );  
run "nmake /f _nt.mak pack_cd SUB_PLAT=$PLAT DR=$plat 2>&1" or die "NMake returned $?";

似乎没有任何效果:(

如果我正确解释system的返回值,请更正我。

1 个答案:

答案 0 :(得分:7)

你有:

use IPC::System::Simple qw(system);
my $exit_status = system ("nmake /f _nt.mak pack_cd SUB_PLAT=$PLAT DR=$plat 2>&1");

鉴于您似乎并不关心实际输出,您可以尝试

my $exit_status = systemx(nmake => 
                              qw(/f _nt.mak pack_cd),
                              "SUB_PLAT=$PLAT",
                              "DR=$plat",
                          );

确保绕过cmd.exe,看看是否有用的东西。

供参考,exit codes from nmake are listed here

运行以下程序:

use strict; use warnings;

use IPC::System::Simple qw(systemx);
use Try::Tiny;

my $status = 0;

try   { systemx nmake => qw(/f bogus) }
catch { ($status) = ( /exit value ([0-9])/ ) };

print "Failed to execute nmake. Exit status = $status\n";

产生

NMAKE : fatal error U1052: file 'bogus' not found
Stop.
Failed to execute nmake. Exit status = 2

以下版本:

use strict; use warnings;

my $status = system nmake => qw(/f bogus);

if ($status) {
    if ($? == -1) {
        print "failed to execute: $!\n";
    }
    elsif ($? & 127) {
        printf "child died with signal %d, %s coredump\n",
        ($? & 127), ($? & 128) ? 'with' : 'without';
    }
    else {
        printf "child exited with value %d\n", $? >> 8;
    }
}

产生

NMAKE : fatal error U1052: file 'bogus' not found
Stop.
child exited with value 2

事实上,即使我使用

my $status = system "nmake /f bogus";

我得到了相同的正确和预期的输出。

当我使用

时同上
my $status = system "nmake /f bogus 2>&1";

这些观察引出了以下问题:

  1. 您使用的是哪个版本的nmake?

  2. /I选项有效吗?即使您没有从命令行设置它,note the following

  3.   

    /I忽略所有命令的退出代码。要为部分makefile设置或清除/I,请使用!CMDSWITCHES。要忽略部分makefile的退出代码,请使用短划线()命令修饰符或.IGNORE。如果指定了两者,则覆盖/K

    所以,我把以下文件放在一起:

    C:\temp> cat test.mak
    test.target: bogus.pl; perl bogus.pl
    C:\temp> cat bogus.pl
    exit 1;

    并且,跑了:

    use strict; use warnings;
    
    my $status = system "nmake /f test.mak 2>&1";
    
    if ($status) {
        if ($? == -1) {
            print "failed to execute: $!\n";
        }
        elsif ($? & 127) {
            printf "child died with signal %d, %s coredump\n",
            ($? & 127), ($? & 128) ? 'with' : 'without';
        }
        else {
            printf "child exited with value %d\n", $? >> 8;
        }
    }
    

    给了我输出:

            perl bogus.pl
    NMAKE : fatal error U1077: 'c:\opt\perl\bin\perl.EXE' : return code '0x1'
    Stop.
    child exited with value 2

    其中最后一行显示nmake的退出状态已正确传播。

    结论:

    您还有其他问题。

    事实上,OP后来在评论中指出:

      

    我尝试运行的实际命令是:system ("nmake /f _nt.mak pack_cd SUB_PLAT=$PLAT DR=$plat 2>&1 | C:\\tee2 $TEMP_DIR\\modules-nt_${platlogfile}");

    鉴于tee参与了管道,nmake退出代码丢失就不足为奇了。 tee已成功处理nmake的输出,因此会返回成功,这是您的脚本看到的退出代码。

    因此,解决方案是自己捕获nmake的输出,使用qx(加上适当的错误检查级别),或使用capture IPC::System::Simple }。然后,您可以决定是否要打印输出,保存到文件,将其放入电子邮件等...