簡體   English   中英

捕獲 Perl 的“system()”的輸出

[英]Capture the output of Perl's 'system()'

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

system('ls')

系統調用將打印到STDOUT ,但我想將輸出捕獲到一個變量中,以便我可以使用我的 Perl 代碼進行未來處理。

這就是反引號的用途。 來自perldoc perlfaq8

為什么我無法使用system()獲取命令的輸出?

您混淆了system()和反引號 (``) 的用途。 system()運行命令並返回退出狀態信息(作為 16 位值:低 7 位是進程死亡的信號,如果有的話,高 8 位是實際退出值)。 反引號 (``) 運行命令並將它發送到 STDOUT 的內容返回。

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

有關詳細信息,請參閱perldoc perlop

IPC::Run是我最喜歡的此類任務模塊。 非常強大和靈活,對於小案例來說也非常簡單。

use IPC::Run 'run';

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

# Now $stdout contains output

只需使用類似於 Bash 示例的方式:

    $variable=`some_command some args`;

就這樣。 請注意,您不會在輸出中看到任何到 STDOUT 的打印,因為它被重定向到一個變量。

此示例不適用於與用戶交互的命令,除非您已准備好答案。 為此,您可以使用一堆 shell 命令來使用類似這樣的東西:

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

answers.txt文件中,您應該為 some_command 正常工作准備所有答案。

我知道這不是最好的編程方式 :) 但這是實現目標的最簡單方式,特別是對於 Bash 程序員。

當然,如果輸出更大(帶有子目錄的ls ),您不應該一次獲得所有輸出。 以與讀取常規文件相同的方式讀取命令:

open CMD,'-|','your_command some args' or die $@;
my $line;
while (defined($line=<CMD>)) {
    print $line; # Or push @table,$line or do whatever 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 an implicit 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 the child code is overloaded by an external command with parameters
    die "Cannot call @CommandCall"; # Error procedure if the call will fail
}

如果您使用這樣的過程,您將捕獲所有過程輸出,並且您可以逐行處理所有內容。 祝你好運:)

我想運行 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 something 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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM