2010-04-26 16 views
9
#!/usr/bin/env perl 
use warnings; use strict; 
use 5.012; 
use IPC::System::Simple qw(system); 

system('xterm', '-geometry', '80x25-5-5', '-bg', 'green', '&'); 

say "Hello"; 
say "World"; 

ho provato questo per eseguire il xterm-comando in background, ma non funziona:Come posso eseguire comandi di sistema Perl in background?

Nessun percorso assoluto trovato per Shell: &

Quale sarebbe il giusto modo di farlo funzionare?

+0

testato qui sembra sorta lavoro per me. cosa stai cercando di realizzare? – xenoterracide

+0

si suppone che apra il terminale e stampi Hello World nel terminale? – xenoterracide

+0

o stai solo provando a fare una chiamata asincrona per aprire il terminale? – xenoterracide

risposta

19

funzione di Perl system ha due modalità:

  1. assumono una singola stringa e passando alla shell di comando per consentire caratteri speciali da trattare
  2. prendendo un elenco di stringhe, eseguendo il primo e passando le stringhe rimanenti come argomenti

Nel primo modulo bisogna fare attenzione a sfuggire ai caratteri che potrebbero avere un significato speciale per la shell. La seconda forma è generalmente più sicura poiché gli argomenti vengono passati direttamente al programma che viene eseguito senza che la shell sia coinvolta.

Nel tuo caso sembra che tu stia mescolando le due forme. Il carattere & ha solo il significato di "avvia questo programma in background" se è passato alla shell. Nel tuo programma, la e commerciale viene passata come quinto argomento al comando xterm.

Come ha detto Jakob Kruse, la semplice risposta è utilizzare la forma a stringa singola di system. Se uno qualsiasi degli argomenti proviene da una fonte non attendibile, è necessario utilizzare la citazione o l'escaping per renderli sicuri.

Se si preferisce utilizzare il modulo a più argomenti, è necessario chiamare fork() e quindi utilizzare probabilmente exec() anziché system().

+4

** tldr; ** 'system (" ./ script.sh $ arg1 &"); 'invece di' system ("./ script.sh", "$ arg1", "&");' – Rombus

13

nota che la forma di lista system è specificamente lì per non caratteri trattare quali & come shell meta-caratteri.

Dalla risposta perlfaq8 s' a How do I start a process in the background?


(contributo di brian d foy)

Non c'è un unico modo per eseguire codice in background in modo da non dover aspettare perché finisca prima che il tuo programma passi ad altre attività. La gestione dei processi dipende dal particolare sistema operativo e molte delle tecniche sono in perlipc.

Diversi moduli CPAN possono essere in grado di aiutare, tra cui IPC::Open2 o IPC::Open3, IPC::Run, Parallel::Jobs, Parallel::ForkManager, POE, Proc::Background e Win32::Process. Ci sono molti altri moduli che potresti usare, quindi controlla anche questi spazi dei nomi per altre opzioni.

Se siete su un sistema Unix-like, si potrebbe essere in grado di cavarsela con una chiamata di sistema in cui si mette un & sulla fine del comando:

system("cmd &") 

Si può anche provare a utilizzare la forcella , come descritto in perlfunc (sebbene questa sia la stessa cosa che molti dei moduli faranno per te).

STDIN, STDOUT e STDERR sono condivisi

Sia il processo principale e quello in background (il processo "figlio") condividono gli stessi filehandle stdin, stdout e stderr. Se entrambi provano ad accedervi contemporaneamente, possono accadere cose strane. Potresti voler chiudere o riaprire questi per il bambino. È possibile aggirare questo problema aprendo una pipe (vedere open in perlfunc) ma su alcuni sistemi ciò significa che il processo figlio non può sopravvivere al genitore. Segnali Dovrete prendere il segnale SIGCHLD e possibilmente anche SIGPIPE. SIGCHLD viene inviato quando il processo in background termina. SIGPIPE viene inviato quando si scrive su un filehandle il cui processo figlio è stato chiuso (un SIGPIPE non mappato può causare la morte silenziosa del programma). Questo non è un problema con il sistema ("cmd &").

Zombies

Devi essere pronto a "raccogliere" il processo figlio quando finisce.

$SIG{CHLD} = sub { wait }; 

$SIG{CHLD} = 'IGNORE'; 

È anche possibile utilizzare una doppia forcella. Attendi immediatamente() per il tuo primo figlio e il demone di init aspetterà() per il tuo nipote una volta che esce.

unless ($pid = fork) { 
    unless (fork) { 
     exec "what you really wanna do"; 
     die "exec failed!"; 
    } 
    exit 0; 
} 
waitpid($pid, 0); 

Vedi Segnali in perlipc per altri esempi di codice per fare questo. Gli zombi non sono un problema con il sistema ("prog &").

0

Questa non è puramente una spiegazione per Perl. Lo stesso problema è in C e in altre lingue.

prima capire che cosa il comando di sistema fa:

  1. Forks
  2. Sotto la chiamata processo figlio exec
  3. Il processo padre è in attesa di processo figlio biforcuta per finire

Non importa se si passano più argomenti o un argomento. La differenza è che, con più argomenti, il comando viene eseguito direttamente.Con un argomento, il comando viene avvolto dalla shell, e infine eseguito come:

/bin/sh -c your_command_with_redirections_and_ambersand 

Quando si passa un comando come some_command par1 par2 &, poi tra l'interprete Perl e il comando è il sh oppure bash processo utilizzato come wrapper, ed è in attesa della finitura some_command. Lo script è in attesa dell'interprete di shell e non è necessario alcun waitpid , poiché il sistema della funzione Perl lo fa per te.

Quando si desidera implementare questo meccanismo direttamente nello script, è necessario:

  1. Utilizzare la funzione forcella. Vedere l'esempio: http://users.telenet.be/bartl/classicperl/fork/all.html
  2. Nella condizione di figlio (if), utilizzare la funzione exec. Il tuo utente è simile al sistema , consulta il manuale. Nota: exec causa la chiusura del programma/contenuto/dati del processo figlio dal comando eseguito.
  3. Nella condizione padre (se, la forcella termina con un valore diverso da zero), si utilizza waitpid, utilizzando il pid restituito dalla funzione di fork.

Ecco perché è possibile eseguire il processo in background. Spero che questo sia semplice.

L'esempio più semplice:

if (my $pid = fork) { #exits 0 = false for child process, at this point is brain split 
    # parent ($pid is process id of child) 
    # Do something what you want, asynchronously with executed command 
    waitpid($pid); # Wait until child ends 
    # If you don't want to, don't wait. Your process ends, and then the child process will be relinked 
    # from your script to INIT process, and finally INIT will assume the child finishing. 
    # Alternatively, you can handle the SIGCHLD signal in your script 
} 
else { 
    # Child 
    exec('some_command arg1 arg2'); #or exec('some_command','arg1','arg2'); 
    #exit is not needed, because exec completely overwrites the process content 
} 
+0

grazie Peter per le correzioni molto buone:) – Znik

Problemi correlati