2010-04-26 13 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"; 

J'ai essayé d'exécuter la commande xterm en arrière-plan, mais il ne fonctionne pas:Comment puis-je exécuter les commandes du système Perl en arrière-plan?

Aucun chemin absolu trouvé pour shell: &

Quel serait le droit façon de le faire fonctionner?

+0

semble sorta travail pour moi. Qu'est-ce que vous essayez d'accomplir? – xenoterracide

+0

est-il censé ouvrir le terminal et ensuite imprimer bonjour monde dans le terminal? – xenoterracide

+0

ou essayez-vous d'effectuer un appel asynchrone pour ouvrir le terminal? – xenoterracide

Répondre

19

fonction de Perl system a deux modes:

  1. prenant une seule chaîne et de la transmettre à l'interpréteur de commandes pour permettre à des caractères spéciaux à traiter
  2. prenant une liste de chaînes, en exécutant le premier et en passant les chaînes restantes comme arguments

Dans le premier formulaire, vous devez faire attention à l'échappement des caractères qui pourraient avoir une signification spéciale pour le shell. La seconde forme est généralement plus sûre puisque les arguments sont passés directement au programme en cours d'exécution sans que le shell ne soit impliqué.

Dans votre cas, vous semblez mélanger les deux formes. Le caractère & a uniquement la signification de "démarrer ce programme en arrière-plan" s'il est transmis au shell. Dans votre programme, l'esperluette est passée en 5ème argument de la commande xterm. Comme Jakob Kruse a dit que la réponse simple est d'utiliser la forme de chaîne unique system. Si l'un des arguments provient d'une source non fiable, vous devrez utiliser la citation ou l'échappement pour les sécuriser.

Si vous préférez utiliser le formulaire multi-arguments, vous devrez appeler fork(), puis probablement utiliser exec() plutôt que system().

+4

** tldr; ** 'system (" ./ script.sh $ arg1 &"); 'au lieu de' system ("./ script.sh", "$ arg1", "&");' – Rombus

13

Notez que la forme de liste des system est spécifiquement là pour pas caractères de traiter tels que & comme méta-caractères shell.

De la réponse de perlfaq8 à How do I start a process in the background?


(contribution de brian d foy)

Il n'y a pas une seule façon d'exécuter du code en arrière-plan afin que vous ne devez pas attendre pour qu'il se termine avant que votre programme passe à d'autres tâches. La gestion des processus dépend de votre système d'exploitation particulier, et de nombreuses techniques sont en perlipc.

Plusieurs modules peuvent être CPAN en mesure d'aider, y compris IPC::Open2 ou IPC::Open3, IPC::Run, Parallel::Jobs, Parallel::ForkManager, POE, Proc::Background et Win32::Process. Il existe de nombreux autres modules que vous pouvez utiliser, donc vérifiez ces espaces de noms pour d'autres options aussi.

Si vous êtes sur un système de type Unix, vous pourriez être en mesure de sortir avec un appel système où vous mettez un & à la fin de la commande:

system("cmd &") 

Vous pouvez également essayer d'utiliser la fourchette , comme décrit dans perlfunc (bien que ce soit la même chose que beaucoup de modules feront pour vous).

STDIN, STDOUT et STDERR sont partagés

Le processus principal et celui backgrounded (le processus "enfant") partagent les mêmes STDIN, STDOUT et STDERR handles de fichiers. Si les deux essayent d'y accéder à la fois, des choses étranges peuvent arriver. Vous voudrez peut-être les fermer ou les rouvrir pour l'enfant. Vous pouvez contourner cela en ouvrant un pipe (voir open in perlfunc) mais sur certains systèmes cela signifie que le processus fils ne peut pas survivre au parent. Signaux Vous devrez attraper le signal SIGCHLD, et éventuellement SIGPIPE. SIGCHLD est envoyé lorsque le processus en arrière-plan se termine. SIGPIPE est envoyé lorsque vous écrivez à un descripteur de fichier dont le processus enfant a été fermé (un SIGPIPE non enfiché peut entraîner la mort de votre programme). Ce n'est pas un problème avec le système ("cmd &").

Zombies

Vous devez être prêt à « récolter » le processus de l'enfant quand il se termine.

$SIG{CHLD} = sub { wait }; 

$SIG{CHLD} = 'IGNORE'; 

Vous pouvez également utiliser une double fourche. Vous attendez immédiatement() pour votre premier enfant, et le démon init attendra() pour votre petit-fils une fois qu'il aura quitté.

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

Voir signaux dans perlipc pour d'autres exemples de code pour le faire. Les zombies ne sont pas un problème avec le système ("prog &").

0

Ceci n'est pas une simple explication de Perl. Le même problème est sous C et d'autres langues.

d'abord comprendre ce que la commande du système fait:

  1. Forks
  2. Dans l'appel de processus enfant exec
  3. Le processus parent attend que le processus d'enfant en forme de fourche pour terminer

Ce n'est pas grave si vous passez plusieurs arguments ou un argument. La différence est, avec plusieurs arguments, la commande est exécutée directement.Avec un argument, la commande est enveloppée par l'enveloppe, et enfin exécuté comme:

/bin/sh -c your_command_with_redirections_and_ambersand 

Lorsque vous passez une commande en tant une_commande par1 par2 &, puis entre l'interpréteur Perl et la commande est le sh ou bash processus utilisé comme un wrapper, et il attend some_command finition. Votre script attend l'interpréteur shell, et aucun waitpid supplémentaire n'est nécessaire, car la fonction Perl système le fait pour vous.

Lorsque vous souhaitez mettre en œuvre ce mécanisme directement dans votre script, vous devez:

  1. Utilisez la fonction fourchette. Voir l'exemple: http://users.telenet.be/bartl/classicperl/fork/all.html
  2. Sous la condition enfant (if), utilisez la fonction exec. Votre utilisateur est similaire au système, voir le manuel. Notez que exec provoque le traitement par l'enfant du programme, du contenu et de la couverture des données par la commande exécutée.
  3. Sous la condition parente (si fork quitte avec une valeur non nulle), vous utilisez waitpid, en utilisant pid retourné par la fonction fork.

C'est pourquoi vous pouvez exécuter le processus en arrière-plan. J'espère que c'est simple.

L'exemple le plus simple: testé ici

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

merci Peter pour de très bonnes corrections:) – Znik