2012-07-17 20 views
21

Perlでsystem()を使ってシェルコマンドを実行する必要があります。例えば、Perlシステムの出力をキャプチャする

system('ls')

システムコールは、STDOUTに出力しますが、私は私のPerlコードで将来の処理を行うことができるように変数に出力をキャプチャしたいです。

+1

システムを使用しないで、パイプでopen()を使用してください。 –

+0

私に例を挙げてもらえますか?ありがとう! – Dagang

+1

'perldoc -f open' –

答えて

32

これはバッククォートのためのものです。 perldoc perlfaq8から:

はなぜsystem()と、コマンドの出力を得ることができませんか?

system()とバッククォート( ``)の目的を混同しています。 system() はコマンドを実行し、終了ステータス情報を返します(16ビット値: )。低位7ビットは、プロセスが終了した信号(存在する場合)で、 上位8ビットが実際の終了値です)。バックチック( ``)はコマンド を実行し、それをSTDOUTに返します。

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

詳細はperldoc perlopを参照してください。

12

IPC::Runはこの種のタスクにとって私のお気に入りのモジュールです。非常に強力で柔軟性があり、小規模なケースでは簡単に簡単です。

$variable=`some_command some args`; 

thatsのすべて:

use IPC::Run 'run'; 

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

# Now $stdout contains output 
+3

Iシェル補間なしでコマンド引数を渡す簡単な方法を探していました。このコマンドからstdoutをキャプチャしました。優秀な答え、ありがとう! –

3

は単に一例をbashに似て使用します。 通知では、STDOUTへの出力は変数にリダイレクトされるので、出力には表示されません。 この例題は、あなたが答えを用意していることを除いて、ユーザと対話するコマンドでは使用できません。そのためには、シェルコマンドのスタックを使用して次のようなものを使用できます。some_commandのすべての回答を正しく準備する必要があります。

私はこれがプログラミングのための最善の方法ではないことを知っています:)しかし、これは目標を達成するための最も簡単な方法、特にbashプログラマーのためのものです。

もちろん、出力が大きい場合(サブディレクトリはls)、一度にすべての出力を取得すべきではありません。あなたがregullarファイルを読むのと同じ方法でコマンドを読む:余分なbashの呼び出しなしで長いコマンドの出力を処理するための

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

追加拡張ソリューション:

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 
} 

あなたがそのような手順を使用している場合、あなたはすべてキャプチャしますプロシージャの出力を作成し、すべてを1行ずつ処理できます。幸運:)

1

私はrsync --progressの出力を見たかったので、バックタックの代わりにsystem()を実行したかったのです。しかし、戻り値によって何かがうまくいかない場合に備えて、出力をキャプチャしたかったのです。 (これはバックアップスクリプト用です)。これは私が今使っているものです:

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; 
    system("rm $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; 
} 
+0

なぜsystem( "rm $ filename")を呼び出しますか?あなたは内部的な手続き$ filenameをunlinkしていますが、shやbashインタプリタを余分に呼び出すことなく同じことをします。 – Znik

関連する問題