【问题标题】:Perl: Capturing correct return value from 'system' commandPerl:从“系统”命令捕获正确的返回值
【发布时间】:2012-05-14 11:39:06
【问题描述】:

我是 Perl 的初学者。我有一个包含多个 NMake 命令的 Windows 批处理脚本。此批处理脚本的一个现有问题是,即使 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;
}

输出:

..... ...... 无法打开目录:R:\TSM_Latest 压缩... NMAKE:致命错误 U1077:'if':返回代码 '0x2' 停止。 孩子以 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 的返回值的解释不正确,请纠正我。

【问题讨论】:

    标签: perl exit-code


    【解决方案1】:

    你有:

    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 : 致命错误 U1052: 找不到文件 'bogus'
    停止。
    执行 nmake 失败。退出状态 = 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 : 致命错误 U1052: 找不到文件 'bogus'
    停止。
    孩子以值 2 退出

    其实就算我用了

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

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

    当我使用时同上

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

    这些观察使我想到以下问题:

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

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

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

    所以,我整理了以下文件:

    C:\temp> cat test.mak
    test.target: bogus.pl; perl bogus.pl
    C:\temp> cat bogus.pl
    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:致命错误 U1077:'c:\opt\perl\bin\perl.EXE':返回代码 '0x1'
    停止。
    孩子以值 2 退出

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

    结论:

    您还有其他问题。

    其实OP后来在cmets中指出:

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

    鉴于tees 参与了管道,nmakes 退出代码丢失也就不足为奇了。 tee 能够成功处理来自 nmake 的输出,因此它返回成功,这就是您的脚本看到的退出代码。

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

    【讨论】:

    • 感谢您对思南的所有帮助!您要求我测试的第一个 systemx 代码(绕过 cmd.exe)运行良好。然而,就在那时,我意识到我做了什么。我试图运行的实际命令是:system ("nmake /f _nt.mak pack_cd SUB_PLAT=$PLAT DR=$plat 2>&1 | C:\\tee2 $TEMP_DIR\\modules-nt_${platlogfile}"); 而不是system ("nmake /f _nt.mak pack_cd SUB_PLAT=$PLAT DR=$plat 2>&1"); 昨天,我无意中删除了日志记录部分(“管道”),认为我应该只发布相关代码。我真的为那个思南感到抱歉! :(
    • 看来 tee 命令总是成功的,所以返回值总是 0。我说的对吗?现在,当我使用“系统”运行我的原始命令时,它按预期工作:use IPC::System::Simple qw(system); system ("nmake /f _nt.mak pack_cd SUB_PLAT=$PLAT DR=$plat > $TEMP_DIR\\modules-nt_${platlogfile}"); 现在我面临的唯一问题是我没有看到任何控制台输出,因为所有内容都被重定向到文件。在这方面有什么帮助吗?
    • 您可以通过单独的 perl 进程或线程来跟踪日志文件。我不知道在 Windows 上是否可以轻松实现。
    猜你喜欢
    • 1970-01-01
    • 2013-08-15
    • 1970-01-01
    • 1970-01-01
    • 2014-08-17
    • 2011-05-23
    • 2012-08-28
    • 2011-04-05
    • 2013-06-25
    相关资源
    最近更新 更多