perl shell

Capture la salida del sistema Perl()



shell (4)

Necesito ejecutar un comando de shell con system () en Perl. Por ejemplo,

system(''ls'')

La llamada al sistema se imprimirá en STDOUT, pero quiero capturar el resultado en una variable para que pueda hacer el procesamiento futuro con mi código Perl.


Para eso son los backticks. De perldoc perlfaq8 :

¿Por qué no puedo obtener el resultado de un comando con system() ?

Estás confundiendo el propósito de system() y backticks (``). system() ejecuta un comando y devuelve información de estado de salida (como un valor de 16 bits: los 7 bits bajos son la señal de la que murió el proceso, si existe, y los 8 bits altos son el valor de salida real). Backticks (``) ejecuta un comando y devuelve lo que envió a STDOUT.

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

Ver perldoc perlop para más detalles.


Quería ejecutar system () en lugar de backtacks porque quería ver el resultado de rsync --progress . Sin embargo, también quería capturar la salida en caso de que algo salga mal según el valor de retorno. (Esto es para un script de respaldo). Esto es lo que estoy usando ahora:

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://.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; }


Simplemente use un ejemplo similar al de bash:

$variable=`some_command some args`;

eso es todo. note, no verá en el resultado ninguna impresión en STDOUT porque esto se redirige a variable. Este ejemplo no se puede utilizar para el comando que interactúa con el usuario, excepto que tiene respuestas preparadas. para eso puedes usar algo como esto usando una pila de comandos de shell:

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

dentro del archivo answers.txt debe preparar todas las respuestas para que some_command funcione correctamente.

Sé que esta no es la mejor manera de programar :), pero esta es la forma más sencilla de lograr el objetivo, especialmente para los programadores de bash.

Por supuesto, si la salida es más grande (ls con subdirectorio), no debería obtener todos los resultados a la vez. Lea el comando de la misma forma que lee el archivo 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;

Solución ampliada adicional para procesar la salida de comando larga sin llamadas extra 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 }

Si utiliza un procedimiento como este, capturará todos los resultados del procedimiento y podrá hacer todo el proceso línea por línea. Buena suerte :)


IPC::Run es mi módulo favorito para este tipo de tareas. Muy potente y flexible, y también trivialmente simple para casos pequeños.

use IPC::Run ''run''; run [ "command", "arguments", "here" ], ">", /my $stdout; # Now $stdout contains output