Si je commence un script qui va prendre beaucoup de temps, je le réalise inévitablement une fois que j'ai commencé le script et je souhaite avoir une façon de faire une sorte d'alerte une fois qu'il est terminé.
Donc, par exemple, si je lance:
really_long_script.sh
et appuyez sur Entrée ... comment puis-je exécuter une autre commande une fois qu'elle est terminée?
Vous pouvez séparer plusieurs commandes par ;
afin qu'elles soient exécutées de manière séquentielle, par exemple:
really_long_script.sh ; echo Finished
Si vous souhaitez exécuter le programme suivant uniquement si le script a terminé avec le code retour 0 (ce qui signifie généralement qu'il s'est correctement exécuté), alors:
really_long_script.sh && echo OK
Si vous voulez l’inverse (c’est-à-dire continuer uniquement si la commande en cours a échoué), alors:
really_long_script.sh || echo FAILED
Vous pouvez exécuter votre script en arrière-plan (mais attention, les scripts générés (stdout
et stderr
) continueront d’être envoyés à votre terminal, sauf si vous le redirigez quelque part), puis wait
pour cela:
really_long_script.sh &
dosomethingelse
wait; echo Finished
Si vous avez déjà exécuté le script, vous pouvez le suspendre avec Ctrl-Z
, puis exécuter quelque chose comme:
fg ; echo Finished
Où fg
affiche le processus suspendu au premier plan (bg
le ferait exécuter en arrière-plan, un peu comme si on avait commencé avec &
)
Vous pouvez également utiliser le contrôle des tâches de bash. Si vous avez commencé
$ really_long_script.sh
puis appuyez sur ctrl + z pour le suspendre:
^Z
[1]+ Stopped really_long_script.sh
$ bg
pour relancer le travail en arrière-plan (comme si vous l'aviez lancé avec really_long_script.sh &
). Ensuite, vous pouvez attendre ce travail en arrière-plan avec
$ wait N && echo "Successfully completed"
où N est l'ID du travail (probablement 1 si vous n'avez exécuté aucun autre travail d'arrière-plan), qui est également affiché sous la forme [1]
ci-dessus.
Si le processus ne s'exécute pas sur le terminal en cours, essayez ceci:
watch -g ps -opid -p <pid>; mycommand
Il s'avère que ce n'est pas si difficile: vous pouvez simplement taper la commande suivante dans la fenêtre pendant que la commande existante est exécutée, appuyez sur entrée, et lorsque la première est terminée, la deuxième commande est exécutée automatiquement.
Je devine qu'il y a des manières plus élégantes, mais cela semble fonctionner.
Modification pour ajouter que si vous souhaitez exécuter une alerte une fois la commande terminée, vous pouvez créer ces alias dans .bashrc, puis exécuter alert
pendant son exécution:
alias alert_helper='history|tail -n1|sed -e "s/^\s*[0-9]\+\s*//" -e "s/;\s*alert$//"'
alias alert='notify-send -i gnome-terminal "Finished Terminal Job" "[$?] $(alert_helper)"'
Il y a quelque temps, j'ai écrit un script pour attendre la fin d'un autre processus. NOISE_CMD
pourrait être quelque chose comme notify-send ...
, étant donné que DISPLAY
est défini correctement.
#!/bin/bash
NOISE_CMD="/usr/bin/mplayer -really-quiet $HOME/sfx/alarm-clock.mp3"
function usage() {
echo "Usage: $(basename "$0") [ PROCESS_NAME [ PGREP_ARGS... ] ]"
echo "Helpful arguments to pgrep are: -f -n -o -x"
}
if [ "$#" -gt 0 ] ; then
PATTERN="$1"
shift
PIDS="$(pgrep "$PATTERN" "$@")"
if [ -z "$PIDS" ] ; then
echo "No process matching pattern \"$PATTERN\" found."
exit
fi
echo "Waiting for:"
pgrep -l "$PATTERN" "$@"
for PID in $PIDS ; do
while [ -d "/proc/$PID" ] ; do
sleep 1
done
done
fi
exec $NOISE_CMD
Sans aucun arrêt, faites du bruit immédiatement. Ce comportement permet quelque chose comme ceci pour plus de commodité (disons que vous appelez le script ci-dessous alarm.sh
):
apt-get upgrade ; ~/bin/alarm.sh
Bien sûr, vous pouvez faire beaucoup de choses amusantes avec un tel script, comme laisser une instance de alarm.sh
attendre une instance de alarm.sh
, qui attend une autre commande. Ou bien exécuter une commande juste après qu'une tâche d'un autre utilisateur connecté est terminée ...>: D
Une ancienne version du script ci-dessus peut être intéressante si vous souhaitez éviter une dépendance à pgrep
et accepter de rechercher vous-même les ID de processus:
#!/bin/bash
if [ "$#" -lt 2 -o ! -d "/proc/$1" ] ; then
echo "Usage: $(basename "$0") PID COMMAND [ ARGUMENTS... ]"
exit
fi
while [ -d "/proc/$1" ] ; do
sleep 1
done
shift
exec "$@"
Légèrement hors sujet, mais utile dans des situations similaires: On pourrait être intéressé par reptyr
, un outil qui "vole" un processus de certains Shell (parent) et l'exécute à partir de Shell actuel. J'ai essayé des implémentations similaires et reptyr
est la plus jolie et la plus fiable pour mes besoins.