Ci sono tre modi principali per lanciare comandi esterni:
system $cmd; # usando system()
$output = `$cmd`; # usando i backtick (``)
open (PIPE, "cmd |"); # usando open()
Con system(), sia STDOUT che STDERR andranno a finire nello stesso posto
in cui andranno a finire lo STDOUT e lo STDERR dello script, a meno che
il comando passato a system() non li rediriga. I backtick e open()
leggono soltanto lo STDOUT del vostro comando.
Potete anche usare la funzione open3() da IPC::Open3. Benjamin
Goldberg ha fornito del codice di esempio:
Per catturare lo STDOUT di un programma, ma scartare il suo STDERR:
use IPC::Open3;
use File::Spec;
use Symbol qw(gensym);
open(NULL, ">", File::Spec->devnull);
my $pid = open3(gensym, \*PH, ">&NULL", "cmd");
while( <PH> ) { }
waitpid($pid, 0);
Per catturare lo STDERR di un programma, ma scartare il suo STDOUT:
use IPC::Open3;
use File::Spec;
use Symbol qw(gensym);
open(NULL, ">", File::Spec->devnull);
my $pid = open3(gensym, ">&NULL", \*PH, "cmd");
while( <PH> ) { }
waitpid($pid, 0);
Per catturare lo STDERR di un programma e far sì che il suo STDOUT vada sul nostro STDERR:
use IPC::Open3;
use Symbol qw(gensym);
my $pid = open3(gensym, ">&STDERR", \*PH, "cmd");
while( <PH> ) { }
waitpid($pid, 0);
Per leggere sia lo STDOUT di un comando che il suo STDERR separatamente, potete
redirezionarli su file temporanei, lasciare che il comando venga eseguito poi leggete
i file temporanei:
use IPC::Open3;
use Symbol qw(gensym);
use IO::File;
local *CATTURAOUT = IO::File->new_tmpfile;
local *CATTURAERR = IO::File->new_tmpfile;
my $pid = open3(gensym, ">&CATTURAOUT", ">&CATTURAERR", "cmd");
waitpid($pid, 0);
seek $_, 0, 0 for \*CATTURAOUT, \*CATTURAERR;
while( <CATTURAOUT> ) {}
while( <CATTURAERR> ) {}
Ma non c'è un vero motivo affinché *entrambi* siano dei file
temporanei... quello che segue dovrebbe funzionare allo stesso modo, senza deadlock:
use IPC::Open3;
use Symbol qw(gensym);
use IO::File;
local *CATTURAERR = IO::File->new_tmpfile;
my $pid = open3(gensym, \*CATTURAOUT, ">&CATTURAERR", "cmd");
while( <CATTURAOUT> ) {}
waitpid($pid, 0);
seek CATTURAERR, 0, 0;
while( <CATTURAERR> ) {}
E sarà anche più veloce, visto che possiamo iniziare ad
elaborare immediatamente lo stdout del programma, piuttosto che aspettare
che il programma finisca.
Con ciascuno di questi, potete modificare il descrittore di file prima
della chiamata:
open(STDOUT, ">logfile");
system("ls");
oppure potete usare la redirezione dei descrittori di file della Bourne shell.
$output = `$cmd 2>un_qualche_file`;
open (PIPE, "cmd 2>un_qualche_file |");
Potete anche usare la redirezione dei descrittori di file per rendere STDERR
un duplicato di STDOUT:
$output = `$cmd 2>&1`;
open (PIPE, "cmd 2>&1 |");
Notate che non si può semplicemente aprire STDERR affinché diventi,
nel vostro programma, un duplicato di STDOUT, evitando di fare ricorso alla
shell per effetturare la redirezione. Questo non funziona:
open(STDERR, ">&STDOUT");
$output_completo = `cmd args`; # stderr continua a sfuggire
Questo fallisce perché open() fa in modo che STDERR vada a finire dove
andava a finire STDOUT al momento della chiamata a open(). I backtick poi
fanno in modo che solo STDOUT vada a finire in una stringa, ma non
modificano STDERR (che continua ad andare laddove andava il vecchio STDOUT).
Notate che per la redirezione, nei backtick, si deve usare la sintassi
della Bourne shell (sh(1)), non quella di csh(1)! Dettagli sul perché
la funzione system(), i backtick e le aperture di pipe usino tutte la
Bourne shell stanno nell'articolo versus/csh.whynot della collezione
"Far More Than You Ever Wanted To Know" ["Molto più di quello che avete
sempre voluto sapere sul Perl", NdT] reperibile all'url
http://www.cpan.org/misc/olddoc/FMTEYEWTK.tgz . Per catturare allo
stesso tempo lo STDOUT e lo STDERR di un comando:
$output = `cmd 2>&1`; # con i backtick
$pid = open(PH, "cmd 2>&1 |"); # o con una pipe
while (<PH>) { } # con relativa lettura
Per catturare lo STDOUT di un comando, buttando via il suo STDERR:
$output = `cmd 2>/dev/null`; # con i backtick
$pid = open(PH, "cmd 2>/dev/null |"); # o con una pipe
while (<PH>) { } # con relativa lettura
Per catturare lo STDERR di un comando, buttando via il suo STDOUT:
$output = `cmd 2>&1 1>/dev/null`; # con i backtick
$pid = open(PH, "cmd 2>&1 1>/dev/null |"); # o con una pipe
while (<PH>) { } # con relativa lettura
Per scambiare lo STDOUT e lo STDERR di un comando, con lo scopo di
catturarne lo STDERR lasciando che il suo STDOUT si sostituisca al nostro STDERR:
$output = `cmd 3>&1 1>&2 2>&3 3>&-`; # con i backtick
$pid = open(PH, "cmd 3>&1 1>&2 2>&3 3>&-|");# o con una pipe
while (<PH>) { } # con relativa lettura
Per leggere lo STDOUT e lo STDERR di un comando separatamente, è
più facile redirigerli su file separatamente,
e poi leggere da questi file quando il programma è terminato:
system("programma args 1>programma.stdout 2>programma.stderr");
L'ordine è importante in tutti questi esempi, poiché la
shell processa le redirezioni di descrittori di file rigorosamente da
sinistra a destra.
system("prog args 1>tmpfile 2>&1");
system("prog args 2>&1 1>tmpfile");
Il primo comando manda sia lo standard out che lo standard error in un file
temporaneo. Il secondo comando manda nel file solo il vecchio standard
output, e il vecchio standard error finisce soltanto nel vecchio
standard out.
|