2012-07-17 8 views
21

Ich muss einen Shell-Befehl mit System() in Perl ausführen. Zum BeispielCapture die Ausgabe von Perl-System()

system('ls')

Der Systemaufruf wird auf STDOUT, aber ich mag die Ausgabe in eine Variable erfassen, so dass wir die zukünftige Verarbeitung mit meinem Perl-Code zu tun.

+1

Verwenden Sie kein System, verwenden Sie offene() mit einem Rohr. –

+0

Können Sie mir ein Beispiel geben? Vielen Dank! – Dagang

+1

'perldoc -f open' –

Antwort

32

Dafür sind Backticks da. Von perldoc perlfaq8:

Warum kann ich die Ausgabe eines Befehls zu erhalten mit system()?

Sie verwirren den Zweck von system() und Backticks (``). system() führt einen Befehl aus und gibt Exit-Statusinformationen zurück (als 16-Bit-Wert: sind die niedrigen 7 Bits das Signal, aus dem der Prozess abgestorben ist, und die hohen 8 Bits sind der tatsächliche Ausgangswert). Backticks (``) führen einen Befehl aus und geben zurück, was es an STDOUT gesendet hat.

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

Siehe perldoc perlop für weitere Details.

12

IPC::Run ist mein Lieblingsmodul für diese Art von Aufgabe. Sehr leistungsfähig und flexibel, und auch für kleine Fälle trivial einfach.

use IPC::Run 'run'; 

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

# Now $stdout contains output 
+3

I habe nach einer trivialen Möglichkeit gesucht, Befehlsargumente ohne Shell-Interpolation zu übergeben und stdout von dem so genannten Befehl zu übernehmen. Ausgezeichnete Antwort, danke! –

3

Verwenden Sie einfach ähnliches Beispiel bash:

$variable=`some_command some args`; 

das ist alles. Beachten Sie, dass bei der Ausgabe keine Ausdrucke auf STDOUT angezeigt werden, da dies zu Variable umgeleitet wird. Dieses Beispiel ist für Befehle, die mit Benutzern interagieren, unbrauchbar, außer Sie haben Antworten vorbereitet.

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

innerhalb answers.txt Datei sollten Sie alle Antworten vorbereiten some_command funktioniert denn, dass Sie so etwas wie dieses mit Stapel von Shell-Befehlen verwenden können.

Ich weiß, das ist nicht der beste Weg für die Programmierung :), aber das ist der einfachste Weg, um das Ziel zu erreichen, speziell für Bash-Programmierer.

Natürlich, wenn die Ausgabe größer ist (ls mit Unterverzeichnis), sollten Sie nicht alle Ausgabe auf einmal erhalten. Lesebefehl von der gleichen Art und Weise, wie Sie regullar Datei lesen:

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; 

Zusätzliche erweiterte Lösung für die Verarbeitung von langer Befehlsausgabe ohne zusätzliche bash calling:

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 
} 

Wenn Sie Prozedur wie das verwenden, erfassen Sie alle Prozedurausgabe, und Sie können die gesamte Verarbeitung Zeile für Zeile durchführen. Viel Glück :)

1

Ich wollte System() statt Backtacks ausführen, weil ich die Ausgabe von rsync --progress sehen wollte. Ich wollte jedoch auch die Ausgabe erfassen, falls etwas schief geht, abhängig vom Rückgabewert. (Dies ist für ein Backup-Skript).Dies ist, was ich jetzt benutze:

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

warum Sie System aufrufen ("rm $ filename")? Sie haben interne Prozedur unlink $ filename, es tut dasselbe ohne zusätzlichen Aufruf sh oder Bash-Interpreter. – Znik