2010-07-07 11 views
7

Ho uno script Perl che svolge alcune attività, una delle quali è chiamare un comando system a "tar -cvf file.tar.....".Mostrare i progressi mentre si esegue un comando system() in Perl

Questo spesso può richiedere un certo tempo così mi piacerebbe la riga di comando per eco indietro di un indicatore di progresso, qualcosa di simile a un # eco torna alla schermata, mentre la chiamata system è in corso.

Ho fatto un po 'di scavo e sono incappato in fork. È questo il modo migliore per andare? È possibile sborsare il comando system, quindi creare un ciclo while che controlla lo stato del $pid restituito dalla forcella?

Ho anche visto i riferimenti a waitpid .... Sto indovinando che ho bisogno di usare anche questo.

fork system("tar ... ") 
while (forked process is still active) { 
    print # 
    sleep 1 
} 

Sto abbaiando dall'albero sbagliato?

Molte grazie John

+0

Il valore di ritorno di 'fork' è l'ID di processo del figlio, se è il genitore, o 0 se è il figlio, o undef se la forcella non è riuscita. Vedi forldoc -f fork. –

risposta

1

vorrei provare qualcosa di simile

open my $tar, "tar -cvf file.tar..... 2>&/dev/null |" 
    or die "can't fork: $!"; 
my $i = 0; 
while (<$tar>) { 
    if(i++ % 1000 == 0) print; 
} 
close $tar or die "tar error: $! $?"; 
+2

Utilizzare i lessici locali per gli handle del file/processo, non globali. – Ether

+0

Il mio perl è un po 'arrugginito, non sapevo che fosse possibile per i filehandle. Grazie, ci proverò la prossima volta. –

7

Perl ha una bella costruzione di questo, chiamato "pipe si apre". Puoi leggere di più a riguardo digitando perldoc -f open al prompt della shell.

# Note the use of a list for passing the command. This avoids 
# having to worry about shell quoting and related errors. 
open(my $tar, '-|', 'tar', 'zxvf', 'test.tar.gz', '-C', 'wherever') or die ...; 

Ecco un frammento che mostra un esempio:

open(my $tar, '-|', 'tar', ...) or die "Could not run tar ... - $!"; 
    while (<$tar>) { 
     print "."; 
    } 
    print "\n"; 
    close($tar); 

Sostituire la print "." con qualcosa che stampa un cancelletto ogni 10 a 100 linee o giù di lì per ottenere un bel gaugebar.

+0

Ciao Johan ... è esattamente quello che sto cercando! Molte grazie per la tua risposta ed esempio. Trovo sempre molto più semplice vedere il codice di esempio. Saluti John – john

+0

una domanda .... perché la virgola sulla linea di stampa? Cosa fa? – john

+0

La virgola non è necessaria, l'ho rimossa dall'esempio ;-) – Johan

6

Un esempio che non dipende dal processo figlio scrittura di qualsiasi tipo di output, e si limita a stampare un punto circa una volta al secondo finché è in esecuzione:

use POSIX qw(:sys_wait_h); 
$|++; 

defined(my $pid = fork) or die "Couldn't fork: $!"; 

if (!$pid) { # Child 
    exec('long_running_command', @args) 
    or die "Couldn't exec: $!"; 
} else { # Parent 
    while (! waitpid($pid, WNOHANG)) { 
    print "."; 
    sleep 1; 
    } 
    print "\n"; 
} 

Anche se probabilmente potrebbe sopportare di avere più controllo degli errori, e ci potrebbe essere qualcosa di meglio già su CPAN. Proc::Background sembra promettente per l'astrazione di questo tipo di lavoro lontano, ma non sono sicuro di quanto sia affidabile.

+0

Ciao e grazie per il tuo aiuto su questo. Darò questo anche a provare e vedere quale metodo si adatta meglio. Mi è piaciuto il tuo suggerimento $ | ++ sopra. Ho fatto una selezione (STDOUT) e poi $ | = 1 – john

1

Per visualizzare i progressi durante un'attività a esecuzione prolungata, è utile il numero Term::ProgressBar, ovvero la funzionalità "stampa di # sullo schermo" descritta.

2
$|++;  
open(my $tar, 'tar ... |') or die "Could not run tar ... - $!"; 
     while ($file=<$tar>) { 
      print "$file"; 
     } 
     print "\n"; 
     close($tar); 

Stampa i nomi file ricevuti da tar.

0

Espandere ciò che Hobbs ha fornito se si desidera ottenere i dati dal processo figlio indietro nel processo Parent è necessario disporre di un conduit esterno. Ho finito per utilizzare i tempfs perché era semplice come un file, ma non inserisce i riscontri di I/O sul disco.

** Importante ** È necessario uscire dal processo secondario, poiché in caso contrario il processo "figlio" continuerà sullo stesso script e verranno visualizzate doppie affermazioni print. Quindi nell'esempio sotto foreach (@stdoutput) accadrebbe due volte, nonostante si trattasse di una sola volta nello script.

$shm_id = time; #get unique name for file - example "1452463743" 
$shm_file = "/dev/shm/$shm_id.tmp"; #set filename in tempfs 
$| = 1; #suffering from buffering 
print ("Activity Indicator: "); #No new line here 
defined(my $pid = fork) or die "Couldn't fork: $!"; 
if (!$pid) { # Child 
    @stdoutput=`/usr/home/script.pl -o $parameter`; #get output of external command 
    open (SHM, ">$shm_file"); 
    foreach (@stdoutput) { 
     print SHM ("$_"); #populate file in tempfs 
    } 
    close (SHM);   
    exit; #quit the child process (will not kill parent script) 
} else { # Parent 
    while (! waitpid($pid, WNOHANG)) { 
    print ("\#"); # prints a progress bar 
     sleep 5; 
    } 
} 
print ("\n"); #finish up bar and go to new line 
open (SHM, "$shm_file"); 
    @stdoutput = <SHM>; #Now open the file and read it. Now array is in parent 
close (SHM); 
unlink ($shm_file); #deletes the tempfs file 
chomp(@stdoutput); 
foreach (@stdoutput) { 
    print ("$_\n"); #print results of external script 
} 
Problemi correlati