捕获Perl系统的输出()

时间:2012-07-17 01:39:50

标签: perl shell

我需要在Perl中使用system()运行shell命令。例如,

system('ls')

系统调用将打印到STDOUT,但我想将输出捕获到变量中,以便我可以使用Perl代码进行将来的处理。

5 个答案:

答案 0 :(得分:42)

这就是反叛的目的。来自perldoc perlfaq8

  

为什么我无法使用system()获取命令的输出?

     

你混淆了system()和反引号(``)的目的。 system()   运行命令并返回退出状态信息(作为16位值:   低7位是进程死亡的信号,如果有的话,和   高8位是实际退出值)。反引号(``)运行命令   并返回它发送给STDOUT的内容。

my $exit_status   = system("mail-users");
my $output_string = `ls`;

有关详细信息,请参阅perldoc perlop

答案 1 :(得分:16)

IPC::Run是我最喜欢的这类任务模块。非常强大和灵活,对于小案例也非常简单。

use IPC::Run 'run';

run [ "command", "arguments", "here" ], ">", \my $stdout;

# Now $stdout contains output

答案 2 :(得分:6)

只需使用类似于bash的例子:

    $variable=`some_command some args`;
这就是全部。 注意,您不会在输出上看到任何打印到STDOUT,因为它被重定向到变量。 除了准备好的答案之外,此示例对于与用户交互的命令无法使用。为此你可以使用一堆shell命令来使用这样的东西:

    $variable=`cat answers.txt|some_command some args`;

在answers.txt文件中你应该为some_command准备好所有答案。

我知道这不是编程的最佳方式:)但这是如何实现目标的最简单方法,特别是对于bash程序员。

当然如果输出更大(带子目录的ls),则不应该立即获得所有输出。通过与读取regullar文件相同的方式读取命令:

open CMD,'-|','your_command some args' or die $@;
my $line;
while (defined($line=<CMD>)) {
    print $line; #or push @table,$line or do whatewer what you want processing line by line
}
close CMD;

用于处理长命令输出的附加扩展解决方案,无需额外的bash调用:

my @CommandCall=qw(find / -type d); #some example single command
my $commandSTDOUT; #file handler
my $pid=open($commandSTDOUT),'-|'); #there will be implict fork!
if ($pid) {
    #parent side
    my $singleLine;
    while(defined($singleline=<$commandSTDOUT>)) {
        chomp $line; #typically we don't need EOL
        do_some_processing_with($line);
    };
    close $commandSTDOUT; #in this place $? will be set for capture
    $exitcode=$? >> 8;
    do_something_with_exit_code($exitcode);
} else {
    #child side, there you really calls a command
    open STDERR, '>>&', 'STDOUT'; #redirect stderr to stdout if needed, it works only for child, remember about fork
    exec(@CommandCall); #at this point child code is overloaded by external command with parameters
    die "Cannot call @CommandCall"; #error procedure if call will fail
}

如果你使用这样的程序,你将捕获所有程序输出,你可以逐行处理所有事情。祝你好运:)

答案 3 :(得分:1)

我想运行system()而不是反引号,因为我想查看rsync --progress的输出。但是,我还希望捕获输出,以防出现问题,具体取决于返回值。 (这是用于备份脚本)。这就是我现在使用的:

use File::Temp qw(tempfile);
use Term::ANSIColor qw(colored colorstrip);



sub mysystem {
    my $cmd = shift; #"rsync -avz --progress -h $fullfile $copyfile";
    my ($fh, $filename) = tempfile();
    # http://stackoverflow.com/a/6872163/2923406
    # I want to have rsync progress output on the terminal AND capture it in case of error.
    # Need to use pipefail because 'tee' would be the last cmd otherwise and hence $? would be wrong.
    my @cmd = ("bash", "-c", "set -o pipefail && $cmd 2>&1 | tee $filename");
    my $ret = system(@cmd);
    my $outerr = join('', <$fh>);
    if ($ret != 0) {
        logit(colored("ERROR: Could not execute command: $cmd", "red"));
        logit(colored("ERROR: stdout+stderr = $outerr", "red"));
        logit(colored("ERROR: \$? = $?, \$! = $!", "red"));
    }
    close $fh;
    unlink($filename);
    return $ret;
}

# and logit() is sth like:
sub logit {
    my $s = shift;
    my ($logsec,$logmin,$loghour,$logmday,$logmon,$logyear,$logwday,$logyday,$logisdst)=localtime(time);
    $logyear += 1900;
    my $logtimestamp = sprintf("%4d-%02d-%02d %02d:%02d:%02d",$logyear,$logmon+1,$logmday,$loghour,$logmin,$logsec);
    my $msg = "$logtimestamp $s\n";
    print $msg;
    open LOG, ">>$LOGFILE";
    print LOG colorstrip($msg);
    close LOG;
}

编辑:使用unlink代替system("rm ...")

答案 4 :(得分:-1)

我过去经常这样做:

print(system("ls"));