J'ai une commande CMD appelée à partir de mon script principal bourne shell qui prend une éternité.
Je souhaite modifier le script comme suit:
- Exécutez la commande CMD en parallèle en tant que processus d'arrière-plan (
CMD &
). - Dans le script principal, créez une boucle pour surveiller la commande générée toutes les quelques secondes. La boucle renvoie également certains messages à stdout indiquant la progression du script.
- Quittez la boucle lorsque la commande générée se termine.
- Capturez et signalez le code de sortie du processus généré.
Quelqu'un peut-il me donner des conseils pour y parvenir?
Réponses:
1: Dans bash,
$!
contient le PID du dernier processus d'arrière-plan qui a été exécuté. Cela vous dira quel processus surveiller, de toute façon.4:
wait <n>
attend que le processus avec PID<n>
soit terminé (il se bloquera jusqu'à ce que le processus se termine, vous ne voudrez peut-être pas l'appeler tant que vous n'êtes pas sûr que le processus est terminé), puis renvoie le code de sortie du processus terminé.2, 3:
ps
oups | grep " $! "
peut vous dire si le processus est toujours en cours d'exécution. C'est à vous de comprendre comment comprendre la sortie et de décider à quel point elle est proche de la finition. (ps | grep
n'est pas à l'épreuve des idiots. Si vous avez le temps, vous pouvez trouver un moyen plus robuste de savoir si le processus est toujours en cours d'exécution).Voici un script squelette:
la source
ps -p $my_pid -o pid=
ni l'un ni l'autregrep
n'est nécessaire.kill -0 $!
est une meilleure façon de savoir si un processus est toujours en cours d'exécution. Il n'envoie en fait aucun signal, vérifie seulement que le processus est actif, en utilisant un shell intégré au lieu de processus externes. Commeman 2 kill
indiqué, "Si sig vaut 0, aucun signal n'est envoyé, mais la vérification des erreurs est toujours effectuée; cela peut être utilisé pour vérifier l'existence d'un ID de processus ou d'un ID de groupe de processus."kill -0
renverra une valeur non nulle si vous n'êtes pas autorisé à envoyer des signaux à un processus en cours d'exécution. Malheureusement, il revient1
à la fois dans ce cas et dans le cas où le processus n'existe pas. Cela le rend utile sauf si vous ne possédez pas le processus - ce qui peut être le cas même pour les processus que vous avez créés si un outil commesudo
est impliqué ou s'ils sont setuid (et éventuellement abandonner les privs).wait
ne renvoie pas le code de sortie dans la variable$?
. Il renvoie simplement le code de sortie et$?
est le code de sortie du dernier programme de premier plan.kill -0
. Voici une référence révisée par les pairs du SO qui montre que le commentaire de CraigRinger est légitime concernant:kill -0
retournera non nul pour les processus en cours d'exécution ... maisps -p
retournera toujours 0 pour tout processus en cours .Voici comment je l'ai résolu quand j'avais un besoin similaire:
la source
wait
s fait attendre le script jusqu'à la toute fin de (chaque) processus.Le pid d'un processus enfant en arrière-plan est stocké dans $! . Vous pouvez stocker tous les pids des processus enfants dans un tableau, par exemple PIDS [] .
Attendez que le processus enfant spécifié par chaque ID de processus pid ou spécification de tâche jobspec se termine et renvoie l'état de sortie de la dernière commande attendue. Si une spécification de tâche est fournie, tous les processus de la tâche sont attendus. Si aucun argument n'est donné, tous les processus enfants actuellement actifs sont attendus et l'état de retour est zéro. Si l'option -n est fournie, wait attend que tout travail se termine et renvoie son état de sortie. Si ni jobspec ni pid ne spécifient un processus enfant actif du shell, l'état de retour est 127.
Utilisez la commande wait, vous pouvez attendre la fin de tous les processus enfants, en attendant, vous pouvez obtenir le statut de sortie de chaque processus enfant via $? et stocker le statut dans STATUS [] . Ensuite, vous pouvez faire quelque chose en fonction du statut.
J'ai essayé les 2 solutions suivantes et elles fonctionnent bien. solution01 est plus concise, tandis que solution02 est un peu compliquée.
solution01
solution02
la source
pid=$!; PIDS[$i]=${pid}; ((i+=1))
peut être écrit plus simplement commePIDS+=($!)
qui ajoute simplement au tableau sans avoir à utiliser une variable distincte pour l'indexation ou le pid lui-même. La même chose s'applique auSTATUS
tableau.Comme je le vois, presque toutes les réponses utilisent des utilitaires externes (principalement
ps
) pour interroger l'état du processus d'arrière-plan. Il existe une solution plus unixesh, captant le signal SIGCHLD. Dans le gestionnaire de signaux, il faut vérifier quel processus fils a été arrêté. Cela peut être fait parkill -0 <PID>
intégré (universel) ou en vérifiant l'existence de/proc/<PID>
répertoire (spécifique à Linux) ou en utilisant lejobs
(frapperspécifique.jobs -l
rapporte également le pid. Dans ce cas, le 3ème champ de la sortie peut être Arrêté | En cours d'exécution | Terminé | Quitter. ).Voici mon exemple.
Le processus lancé est appelé
loop.sh
. Il accepte-x
ou un nombre comme argument. Pour-x
est sort avec le code de sortie 1. Pour un nombre, il attend num * 5 secondes. Toutes les 5 secondes, il imprime son PID.Le processus de lancement s'appelle
launch.sh
:Pour plus d'explications, voir: Le démarrage d'un processus à partir d'un script bash a échoué
la source
for i in ${!pids[@]};
utilisant l'expansion des paramètres.la source
grep -v
. Vous pouvez limiter la recherche au début de la ligne:grep '^'$pid
De plus, vous pouvez le faire deps p $pid -o pid=
toute façon. De plus, celatail -f
ne se terminera pas tant que vous ne l'aurez pas tué, donc je ne pense pas que ce soit un très bon moyen de faire une démonstration (du moins sans le souligner). Vous voudrez peut-être rediriger la sortie de votreps
commande vers/dev/null
ou elle ira à l'écran à chaque itération. Votreexit
faitwait
sauter le - il devrait probablement s'agir d'un fichierbreak
. Mais lewhile
/ps
et le ne sont-ils paswait
redondants?kill -0 $pid
t- il ? Il n'envoie en fait aucun signal, vérifie seulement que le processus est actif, en utilisant un shell intégré au lieu de processus externes.bash: kill: (1) - Operation not permitted
Je changerais légèrement votre approche. Plutôt que de vérifier toutes les quelques secondes si la commande est toujours active et de signaler un message, ayez un autre processus qui signale toutes les quelques secondes que la commande est toujours en cours d'exécution, puis arrêtez ce processus lorsque la commande se termine. Par exemple:
la source
while kill -0 $pid 2> /dev/null; do X; done
, j'espère que c'est utile pour quelqu'un d'autre dans le futur qui lira ce message;)Notre équipe avait le même besoin avec un script exécuté par SSH à distance qui expirait après 25 minutes d'inactivité. Voici une solution avec la boucle de surveillance vérifiant le processus d'arrière-plan toutes les secondes, mais n'imprimant que toutes les 10 minutes pour supprimer un délai d'inactivité.
la source
Un exemple simple, similaire aux solutions ci-dessus. Cela ne nécessite de surveiller aucune sortie de processus. L'exemple suivant utilise tail pour suivre la sortie.
Utilisez tail pour suivre la sortie du processus et quittez lorsque le processus est terminé.
la source
Une autre solution consiste à surveiller les processus via le système de fichiers proc (plus sûr que le combo ps / grep); lorsque vous démarrez un processus, il a un dossier correspondant dans / proc / $ pid, donc la solution pourrait être
Vous pouvez maintenant utiliser la variable $ exit_status comme vous le souhaitez.
la source
Syntax error: "else" unexpected (expecting "done")
Avec cette méthode, votre script n'a pas à attendre le processus d'arrière-plan, vous n'aurez qu'à surveiller un fichier temporaire pour l'état de sortie.
maintenant, votre script peut faire autre chose alors que vous n'avez plus qu'à continuer à surveiller le contenu de retFile (il peut aussi contenir toute autre information que vous voulez comme l'heure de sortie).
PS: btw, j'ai codé la pensée en bash
la source
Cela peut aller au-delà de votre question, mais si vous êtes préoccupé par la durée d'exécution des processus, vous voudrez peut-être vérifier l'état des processus en arrière-plan en cours d'exécution après un certain temps. Il est assez facile de vérifier quels PID enfants sont toujours en cours d'exécution
pgrep -P $$
, mais j'ai proposé la solution suivante pour vérifier l'état de sortie de ces PID qui ont déjà expiré:qui sort:
Remarque: vous pouvez passer
$pids
à une variable chaîne plutôt qu'à un tableau pour simplifier les choses si vous le souhaitez.la source
Ma solution était d'utiliser un tube anonyme pour transmettre l'état à une boucle de surveillance. Il n'y a pas de fichiers temporaires utilisés pour échanger le statut, donc rien à nettoyer. Si vous n'êtes pas sûr du nombre de travaux en arrière-plan, la condition de rupture pourrait être
[ -z "$(jobs -p)" ]
.la source