Perl:从“系统"命令捕获正确的返回值 [英] Perl: Capturing correct return value from 'system' command

查看:63
本文介绍了Perl:从“系统"命令捕获正确的返回值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是 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");如果 ( $? == -1 ) {print "命令执行失败:$!\n";}elsif ( $? & 127 ) {printf "孩子因信号 %d 死亡,%s 为核心转储\n",( $? & 127 ), ( $? & 128 ) ?'有没有';}别的 {printf "子进程退出,值为 %d\n", $?>>8;}

输出:

<前>..........无法打开目录:R:\TSM_Latest压缩...NMAKE:致命错误U1077:'if':返回代码'0x2'停止.孩子以 0 值退出

也尝试过:

use IPC::System::Simple qw(system);我的 $exit_status = system ("nmake/f _nt.mak pack_cd SUB_PLAT=$PLAT DR=$plat 2>&1");如果($exit_status != 0){打印失败";3号出口;} 别的 {打印成功";}

终于尝试了以下模块:

use IPC::Run qw(run timeout);运行nmake/f _nt.mak pack_cd SUB_PLAT=$PLAT DR=$plat 2>&1"或者死NMake返回$?";

似乎没有任何效果:(

如果我对 system 的返回值解释有误,请纠正我.

解决方案

您有:

use IPC::System::Simple qw(system);我的 $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,看看你是否得到了一些有用的东西.

作为参考,来自 nmake 的 退出代码列在此处.

运行以下程序:

使用严格;使用警告;使用 IPC::System::Simple qw(systemx);使用 Try::Tiny;我的 $status = 0;尝试 { systemx nmake =>qw(/f 假的) }catch { ($status) = (/exit value ([0-9])/) };print "执行 nmake 失败.退出状态 = $status\n";

产生:

NMAKE:致命错误 U1052:未找到文件伪造"停止.无法执行 nmake.退出状态 = 2

以下版本:

使用严格;使用警告;我的 $status = system nmake =>qw(/f 假的);如果($状态){如果($?== -1){打印执行失败:$!\n";}elsif ($? & 127) {printf "孩子因信号 %d 死亡,%s 核心转储\n",($? & 127), ($? & 128) ?'有没有';}别的 {printf "子进程退出,值为 %d\n", $?>>8;}}

产生:

NMAKE:致命错误 U1052:未找到文件伪造"停止.孩子以值 2 退出

事实上,即使我使用

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

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

我使用时同上

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

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

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

  2. /I 选项有效吗?即使您没有从命令行设置它,注意以下事项:

<块引用>

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

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

C:\temp> cat test.mak测试目标:bogus.pl;perl bogus.pl

C:\temp> cat bogus.pl退出 1;

然后,跑了:

使用严格;使用警告;我的 $status = system "nmake/f test.mak 2>&1";如果($状态){如果($?== -1){打印执行失败:$!\n";}elsif ($? & 127) {printf "孩子因信号 %d 死亡,%s 核心转储\n",($? & 127), ($? & 128) ?'有没有';}别的 {printf "子进程退出,值为 %d\n", $?>>8;}}

这给了我输出:

 perl bogus.plNMAKE:致命错误 U1077:c:\opt\perl\bin\perl.EXE":返回代码0x1"停止.child 以值 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.然后,您可以决定是否要打印该输出、保存到文件、将其放入电子邮件等 …

I'm a beginner in Perl. I have a Windows batch script which contains multiple NMake commands. An existing issue with this batch script is that even if the NMake command fails during its execution, ERRORLEVEL doesn't get set properly.

So we never know whether the command worked until we parse the log file. I looked into it but couldn't find a solution. I, then thought of converting this batch script to a Perl script assuming that trapping error will be easier but it seems it's not that easy :)

Whenever I run my Perl script, the 'system' command always returns 0. I looked at many different links, and realized that capturing the correct return status of 'system' command is not that straightforward. Still, I tried the suggestions but things are not working. :(

Let me mention that the NMake command that is called, in turn, calls many different commands during its execution. For instance, the command output mentioned below, which is throwing 'fatal error', is actually part of a Perl script (check_dir.pl). This call to Perl script is written in the NMake file itself.

If I call this Perl file (check_dir.pl) directly and check for exit value, I get correct result i.e., the command fails and prints a non-zero exit value (...unexpectedly returned exit value 2).

Tried Perl's system function but it didn't help. I used the following code:

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;
}

Output:

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

Also tried:

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";  
}

Finally tried the following module:

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

Nothing seems to be working :(

Please correct me if i'm interpreting the return value of system incorrectly.

解决方案

You have:

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

Given that you don't seem to care about the actual output, you can try

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

To make sure you bypass cmd.exe and see if you get something useful.

For reference, the exit codes from nmake are listed here.

Running the following program:

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";

produces:

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

The following version:

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;
    }
}

produces:

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

In fact, even when I use

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

I get the same correct and expected output.

Ditto when I use

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

These observations lead me to the following questions:

  1. Which version of nmake are you using?

  2. Is the /I option in effect? Even though you don't set it from the command line, note the following:

/I Ignores exit codes from all commands. To set or clear /I for part of a makefile, use !CMDSWITCHES. To ignore exit codes for part of a makefile, use a dash () command modifier or .IGNORE. Overrides /K if both are specified.

So, I put together the following files:

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

C:\temp> cat bogus.pl
exit 1;

And, ran:

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;
    }
}

which gave me the output:

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

where the last line shows that the exit status of nmake was correctly propagated.

Conclusion:

You have some other problem.

In fact, the OP later pointed out in comments that:

The actual command that i am trying to run is: system ("nmake /f _nt.mak pack_cd SUB_PLAT=$PLAT DR=$plat 2>&1 | C:\\tee2 $TEMP_DIR\\modules-nt_${platlogfile}");

Given tees involvement in the pipeline, it is not surprising that nmakes exit code gets lost. tee is successfully able to process output from nmake, so it returns success, and that's the exit code your script sees.

Therefore, the solution is to capture the output of nmake yourself, either using qx (coupled with the appropriate level of error checking), or using capture from IPC::System::Simple. Then, you can decide to whether you want to print that output, save to a file, put it in an email etc …

这篇关于Perl:从“系统"命令捕获正确的返回值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆