Comment exécuter une commande après la fin d'une commande déjà en cours, existante?

32

Si je démarre un script qui va prendre beaucoup de temps, je m'en rends compte inévitablement après avoir commencé le script, et j'aurais aimé avoir un moyen de faire une sorte d'alerte une fois qu'il est terminé.

Ainsi, 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?

mlissner
la source

Réponses:

45

Vous pouvez séparer plusieurs commandes en les exécutant 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 s'est terminé avec le code retour 0 (ce qui signifie généralement qu'il s'est exécuté correctement), alors:

really_long_script.sh && echo OK

Si vous voulez le contraire (c'est-à-dire continuer uniquement si la commande actuelle a échoué), alors:

really_long_script.sh || echo FAILED

Vous pouvez exécuter votre script en arrière-plan (mais attention, la sortie des scripts ( stdoutet stderr) continuerait à aller vers votre terminal à moins que vous ne le redirigiez quelque part), puis waitpour 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

fgamène le processus suspendu au premier plan (le bgferait fonctionner en arrière-plan, un peu comme au début &)

un terrain
la source
@mlissner Développé ma réponse pour couvrir ce cas
aland
8
Puisque fgrenvoie la valeur du travail qu'il reprend, vous pouvez l'utiliser fg && echo Finishedsi vous voulez vous assurer que la commande réussit avant d'exécuter l'autre commande.
palswim
13

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 redémarrer le travail en arrière-plan (comme si vous l'aviez démarré really_long_script.sh &). Ensuite, vous pouvez attendre ce travail de fond avec

$ wait N && echo "Successfully completed"

où N est l'ID du travail (probablement 1 si vous n'avez exécuté aucun autre travail en arrière-plan) qui est également affiché comme [1]ci-dessus.

Jaap Eldering
la source
11

Si le processus ne s'exécute pas sur le terminal actuel, essayez ceci:

watch -g ps -opid -p <pid>; mycommand
Marinos An
la source
2
J'adore l'utilisation créative de watch. Réponse super courte et on apprend une nouvelle façon utile d'utiliser la montre
Piotr Czapla
2

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 s'exécute, appuyez sur Entrée et lorsque la première se termine, la deuxième commande s'exécute automatiquement.

Je suppose qu'il existe des moyens plus élégants, 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 alertpendant 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)"'
mlissner
la source
7
Sauf si le script en cours d'exécution attend une entrée à un moment donné.
Daniel Beck
@Daniel - ouais ... c'est un problème. Tirer.
mlissner
1

Il y a quelque temps, j'ai écrit un script pour attendre la fin d'un autre processus. NOISE_CMDpourrait être quelque chose comme notify-send ..., étant donné qu'il DISPLAYest correctement défini.

#!/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 segment, faites juste du bruit immédiatement. Ce comportement permet quelque chose comme ça 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 drôles avec un tel script, comme laisser une instance d' alarm.shattendre une instance de alarm.sh, c'est-à-dire attendre une autre commande. Ou exécuter une commande juste après la fin de la tâche d'un autre utilisateur connecté ...>: D

Une ancienne version du script ci-dessus peut être intéressante, si vous voulez éviter une dépendance pgrepet 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 d'un shell (parent) et l'exécute à partir du shell actuel. J'ai essayé des implémentations similaires et reptyrc'est la plus jolie et la plus fiable pour mes besoins.

Piscoris
la source