2009-11-17 9 views
9

Attualmente ho uno script Perl che esegue un comando esterno sul sistema, raccoglie l'output ed esegue un'azione in base a ciò che è stato restituito. In questo momento, ecco come ho eseguito questo (dove $ cmd è una stringa con il comando setup):Come posso eseguire un comando di sistema in Perl in modo asincrono?

@output = `$cmd`; 

mi piacerebbe cambiare questo modo, se si blocca di comando e non restituisce un valore dopo tanto ora poi uccido il comando. Come dovrei fare per eseguire questo in modo asincrono?

risposta

8

Se si ha davvero bisogno di mettere un timeout su una determinata chiamata di sistema è un problema molto più semplice della programmazione asincrona.

Tutto ciò di cui hai bisogno è alarm() all'interno di un blocco eval().

Ecco un esempio di blocco di codice che inserisce questi in una subroutine che è possibile inserire nel codice. L'esempio chiama sonno in modo non è entusiasmante per l'uscita, ma non si mostrano le funzionalità di timeout si erano interessati a Output di esso è:.

/bin/sleep 2 fallimento: timeout a ./time linea -out 15.

$ cat time-out 
#!/usr/bin/perl 

use warnings; 
use strict; 
my $timeout = 1; 
my @cmd = qw(/bin/sleep 2); 
my $response = timeout_command($timeout, @cmd); 
print "$response\n" if (defined $response); 

sub timeout_command { 
     my $timeout = (shift); 
     my @command = @_; 
     undef [email protected]; 
     my $return = eval { 
       local($SIG{ALRM}) = sub {die "timeout";}; 
       alarm($timeout); 
       my $response; 
       open(CMD, '-|', @command) || die "couldn't run @command: $!\n"; 
       while(<CMD>) { 
         $response .= $_; 
       } 
       close(CMD) || die "Couldn't close execution of @command: $!\n"; 
       $response; 
     }; 
     alarm(0); 
     if ([email protected]) { 
       warn "@cmd failure: [email protected]\n"; 
     } 
     return $return; 
} 
+0

Grazie, questo è più quello che stavo cercando. – Joel

+0

Penso che avrai bisogno di un \ n nel messaggio del dado. (Http://perldoc.perl.org/functions/alarm.html) – ddoxey

12

ci sono un sacco di modi per farlo:

  • È possibile farlo con una forchetta (perldoc -f forcella)
  • o utilizzando le discussioni (thread perldoc). Entrambi rendono difficile il ritorno delle informazioni restituite al programma principale.
  • Sui sistemi che lo supportano, è possibile impostare un allarme (perldoc -f allarme) e quindi eseguire la pulizia nel gestore di segnale.
  • È possibile utilizzare un ciclo di eventi come POE o Coro.
  • Invece dei backtick, è possibile utilizzare open() o rispettivamente open2 o open3 (vedere IPC :: Open2, IPC :: Open3) per avviare un programma mentre si ottiene STDOUT/STDERR tramite un handle di file. Esegui operazioni di lettura non bloccanti su di esso. (perldoc -f select e probabilmente google "perl nonblocking read")
  • Come variante più potente di openX(), controlla IPC :: Esegui/IPC :: Cmd.
  • Probabilmente tonnellate a cui non riesco a pensare nel cuore della notte.
+3

nei sistemi Win32 guardare fuori per rotta 'select' che funziona solo per prese e le interazioni potenzialmente strane con finestre di console, wperl (corre perl in modo finestra non da console, ma ha effetti bizzarri su STDIO e sui processi figli). Sul lato positivo puoi fare un 'system (-1, 'pippo')'. Inoltre, alcuni modi di avviare un processo falliscono dopo l'esecuzione di 64 bambini. IME, 'Win32 :: Process :: Create()' è il modo più sicuro per eseguire un programma in bgrnd. – daotoad

+0

@daotoad, grazie per quel commento.La mia mancanza di informazioni su win32 dimostra che l'unica cosa che so è che fork() viene emulato con thread e non è possibile utilizzare l'allarme. Suppongo che se Win32 è coinvolto, IPC :: Cmd sarebbe una buona scelta. – tsee

1

Se il programma esterno non si assume alcun input, cercare le seguenti parole del perlipc manpage:

Ecco un apice inverso di sicurezza o tubo aperto di lettura:

Usa il codice di esempio e custodirlo con un allarme (che viene anche spiegato in perlipc).

Problemi correlati