我该如何在Perl中获取外部命令的输出?

23

我希望将Windows命令行程序(如powercfg -l)的输出写入一个由Perl创建的文件中,然后在一个for循环中逐行读取文件并将其赋值给一个字符串。


请在问题评论中更新所有细节,以便人们能够更好地帮助您。您获得的错误消息和您正在处理的示例将有所帮助。 - Eric Schoonover
7个回答

28

你已经有一些很好的回答了。此外,如果你只是想处理一个命令的输出,而不需要直接将该输出发送到文件中,你可以在命令和你的Perl脚本之间建立一个管道。

use strict;
use warnings;

open(my $fh, '-|', 'powercfg -l') or die $!;
while (my $line = <$fh>) {
    # Do stuff with each $line.
}

4
在使用while语句读取文件句柄时,无需进行定义测试。请参见 perl -MO=Deparse -e "print while <>" 的输出结果。 - Sinan Ünür
@Sinan 非常感谢。我之前不熟悉 -MO=Deparse,非常有用。 - FMc
这很好,因为如果命令产生了大量输出并开始交换,我的$var=command可能会失控。虽然不是典型情况,但确实会发生。 - Michael Conlen

25
system 'powercfg', '-l';

这是推荐的方法。如果您不介意生成一个子shell,

system "powercfg -l";

也会起作用。如果您希望将结果存储为字符串:

my $str = `powercfg -l`;

1
不要忘记检查系统调用的返回值。 - Craig
1
@craig:有没有办法检查返回值,并将输出捕获到一个字符串中? - jimtut
6
请使用反引号,然后检查$?的值。 - innaM
1
如果你想在不生成子shell的情况下捕获输出:open my $pipe, '-|', qw(powercfg -l) or die; my @output = <$pipe>; close $pipe; - ephemient
你好, 如何将命令结果捕获到字符串中? 上述方法都不起作用! 请建议其他方法来完成此操作? - stack_pointer is EXTINCT
显示剩余5条评论

23
my $output = qx(powercfg -l);

## You've got your output loaded into the $output variable. 
## Still want to write it to a file?
open my $OUTPUT, '>', 'output.txt' or die "Couldn't open output.txt: $!\n";
print $OUTPUT $output;
close $OUTPUT

## Now you can loop through each line and
##   parse the $line variable to extract the info you are looking for.
foreach my $line (split /[\r\n]+/, $output) {
  ## Regular expression magic to grab what you want
}

9

不需要先将命令的输出保存在文件中:

my $output = `powercfg -l`;

参见引号操作符中的qx//

但是,如果您想先将输出保存到文件,则可以使用:

my $output_file = 'output.txt';

system "powercfg -l > $output_file";

open my $fh, '<', $output_file 
    or die "Cannot open '$output_file' for reading: $!";

while ( my $line = <$fh> ) {
    # process lines
}

close $fh;

请查看perldoc -f system

2

由于OP正在运行powercfg,他们可能正在捕获外部脚本的输出,因此他们可能不会发现这个答案非常有用。这篇文章主要是为了其他通过搜索找到这里的人而写的。

本答案描述了几种启动命令的方法,这些命令将在后台运行,而不会阻止您的脚本进一步执行。

请查看perlport入口的system。您可以使用system(1,'要运行的命令行');来生成一个子进程并继续执行您的脚本。

这真的非常方便,但是有一个严重的警告没有被记录下来。如果您在一次脚本执行中启动了超过64个进程,则尝试运行其他程序将失败。

我已经验证了Windows XP和ActivePerl 5.6和5.8的情况。我没有测试过Vista或Stawberry Perl,也没有测试过任何5.10版本。

这是一个一行代码,可以用来测试您的Perl是否存在此问题:

C:\>perl -e "for (1..100) { print qq'\n $_\n-------\n'; system( 1, 'echo worked' ), sleep 1 }

如果您的系统存在问题,并且将要启动许多程序,您可以使用Win32::Process模块来管理应用程序的启动。
以下是使用Win32::Process的示例:
use strict;
use warnings;

use Win32::Process;

if( my $pid = start_foo_bar_baz() ) {
    print "Started with $pid";
}
:w

sub start_foo_bar_baz {

    my $process_object;  # Call to Create will populate this value.
    my $command = 'C:/foo/bar/baz.exe';  # FULL PATH to executable.
    my $command_line = join ' ',
           'baz',   # Name of executable as would appear on command line
           'arg1',  # other args
           'arg2';

    # iflags - controls whether process will inherit parent handles, console, etc.
    my $inherit_flags = DETACHED_PROCESS;  

    # cflags - Process creation flags.
    my $creation_flags = NORMAL_PRIORITY_CLASS;

    # Path of process working directory
    my $working_directory = 'C:/Foo/Bar';

    my $ok = Win32::Process::Create(
       $process_object,
       $command,
       $command_line,
       $inherit_flags,
       $creation_flags, 
       $working_directory,
    );

    my $pid;
    if ( $ok ) {
        $pid = $wpc->GetProcessID;
    }
    else {
        warn "Unable to create process: "
             . Win32::FormatMessage( Win32::GetLastError() ) 
        ;
        return;
    }

    return $pid;
}

2

以下是对Sinan优秀答案的拓展,更明确地回答您的问题:

注意:反引号``告诉Perl执行一个命令并检索其输出:

#!/usr/bin/perl -w
use strict;


my @output = `powercfg -l`;
chomp(@output); # removes newlines

my $linecounter = 0;    
my $combined_line;

foreach my $line(@output){
    print $linecounter++.")";
    print $line."\n"; #prints line by line

    $combined_line .= $line; # build a single string with all lines
    # The line above is the same as writing:
    # $combined_line = $combined_line.$line;
}

print "\n";
print "This is all on one line:\n";
print ">".$combined_line."<";

您的输出(在我的系统上)将是:

您的输出(在我的系统上)将是:

0)
1)Existing Power Schemes (* Active)
2)-----------------------------------
3)Power Scheme GUID: 381b4222-f694-41f0-9685-ff5bb260df2e  (Balanced) *
4)Power Scheme GUID: 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c  (High performance)
5)Power Scheme GUID: a1841308-3541-4fab-bc81-f71556f20b4a  (Power saver)

This is all on one line:
>Existing Power Schemes (* Active)-----------------------------------Power Scheme GUID: 381b4222-f694-41f0-9685-ff5bb260df2e  (Balanced) *Power Scheme GUID: 8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c  (High performance)Power Scheme GUID: a1841308-3541-4fab-bc81-f71556f20b4a  (Power saver)<
< p > Perl让它变得容易!< /p >

1

尝试使用 > 运算符将输出转发到文件中,例如:

powercfg -l > output.txt

然后打开 output.txt 并处理它。


嗨,我想在这里使用Perl...因为我不知道如何将输出捕获到一个文件(要创建的)中,并逐行读取该文件并将该行分配给PERL脚本中的字符串。 - stack_pointer is EXTINCT

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接