Comment exécuter des commandes système Perl en arrière-plan?

#!/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é de lancer la commande xterm-en arrière-plan, mais ça ne marche pas:

Pas de chemin d'accès absolu trouvé pour le shell: &

Quelle serait la bonne façon de le faire fonctionner?

10
demandé sur Borodin 2010-04-26 10:42:51

4 réponses

Perl fonction a deux modes:

  1. prendre une seule chaîne de caractères et la passer à l'interpréteur de commandes pour permettre le traitement de caractères spéciaux
  2. prendre une liste de chaînes, passer la première et passer les chaînes restantes comme arguments

dans la première forme, vous devez faire attention à éviter les caractères qui pourraient avoir une signification particulière pour le shell. La deuxième forme est généralement plus sûre puisque les arguments sont passés directement au programme exec avais sans la coquille.

dans votre cas, vous semblez mélanger les deux formes. & le caractère n'a la signification de "démarrer ce programme en arrière-plan" que s'il est passé à l'interpréteur de commandes. Dans votre programme, l'esperluette est passé 5ème argument de la commande xterm.

comme Jakob Kruse l'a dit la réponse simple est d'utiliser la forme de chaîne simple de system. Si l'un des arguments est venu de confiance source vous devez utiliser la citation ou l'évasion pour les rendre sûrs.

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

19
répondu Grant McLean 2010-04-26 16:30:30

Notez que la liste des est précisément là pour traiter des caractères tels que & shell méta-caractères.

perlfaq8réponse de Comment lancer un processus en arrière-plan?


(contribution de brian d foy)

il n'y a pas une seule façon d'exécuter le code en arrière-plan donc vous n'avez pas à attendre qu'il soit terminé avant votre programme passe à d'autres tâches. La gestion des processus dépend de votre système d'exploitation, et de nombreuses techniques sont en perlipc.

plusieurs modules CPAN peuvent être utiles, y compris IPC:: Open2 ou IPC:: Open3,IPC:: Run,Parallèle::Emplois,Parallèle::ForkManager, POE,Proc::Arrière-Plan et Win32:: Process. Il existe de nombreux autres modules 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 s'en tirer avec un système d'appel où vous mettez un & à la fin de la commande:

system("cmd &")

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

STDIN, STDOUT, and STDERR are shared

tant le processus principal que le processus de base un (le processus "enfant") partage les mêmes fichiers STDIN, STDOUT et STDERR. Si les deux tentent d'y accéder en même temps, des choses étranges peuvent se produire. Vous pouvez fermer ou rouvrir ces pour l'enfant. Vous pouvez contourner cela en ouvrant une pipe (voir Ouvrir dans perlfunc) mais sur certains systèmes cela signifie que le processus enfant ne peut pas survivre au parent. Signal Vous devez capter le signal SIGCHLD, et peut-être SIGPIPE aussi. SIGCHLD est envoyé lorsque le processus de finition à l'arrière-plan. SIGPIPE est envoyé quand vous écrivez à un filehandle dont le processus enfant est fermé (un SIGPIPE non retenu peut causer la mort silencieuse 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 fourchette. Vous attendez immédiatement () pour votre premier enfant, et le démon init attendra () pour votre petit-enfant une fois qu'il sort.

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

Voir Signaux perlipc pour d'autres exemples de code pour faire cela. Zombies ne sont pas un problème avec le système("prog &").

12
répondu brian d foy 2010-05-26 02:52:48

Avez-vous essayé?

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

http://www.rocketaware.com/perl/perlfaq8/How_do_I_start_a_process_in_the_.htm

4
répondu Jakob Kruse 2010-04-26 06:56:05

ce n'est pas une simple explication pour Perl. Le même problème se pose sous C et dans d'autres langues.

d'Abord comprendre ce que l' commande:

  1. Fourches
  2. sous le processus enfant appel exec
  3. Le processus parent est en attente pour fourche enfant fin du processus

cela n'a pas d'importance 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 le shell, et finalement exécutée comme:

/bin/sh -c your_command_with_redirections_and_ambersand

quand vous passez une commande comme some_command par1 par2 &, puis entre l'interpréteur Perl et la commande est sh ou bash processus utilisé comme une enveloppe, et il est en attente d' some_command finition. Votre script attend l'interpréteur de commandes, et pas d'interpréteur de commandes supplémentaire. waitpid est nécessaire, parce que la fonction de Perl le fait pour vous.

quand vous voulez implémenter ce mécanisme directement dans votre script, vous devez:

  1. fourche fonction. Voir l'exemple: http://users.telenet.be/bartl/classicperl/fork/all.html
  2. sous la condition de l'enfant (if),exec fonction. Votre utilisateur est similaire à , voir le manuel. Remarquez,exec provoque le processus enfant programme / contenu / couverture des données par la commande exécutée.
  3. dans la condition parent (si, fork sort avec non-zero), vous utilisez waitpid, en utilisant le 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:

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
répondu Znik 2017-01-14 08:53:54