¿Cómo ejecutar un comando después de que finalice uno existente?

32

Si comienzo un guión que llevará mucho tiempo, inevitablemente me doy cuenta después de haber comenzado el guión, y desearía tener una forma de hacer algún tipo de alerta una vez que termine.

Entonces, por ejemplo, si ejecuto:

really_long_script.sh

y presione enter ... ¿cómo puedo ejecutar otro comando una vez que finalice?

mlissner
fuente

Respuestas:

45

Puede separar varios comandos por ;, para que se ejecuten secuencialmente, por ejemplo:

really_long_script.sh ; echo Finished

Si desea ejecutar el siguiente programa solo si el script terminó con el código de retorno 0 (lo que generalmente significa que se ha ejecutado correctamente), entonces:

really_long_script.sh && echo OK

Si desea lo contrario (es decir, continúe solo si el comando actual ha fallado), entonces:

really_long_script.sh || echo FAILED

Podría ejecutar su script en segundo plano (pero tenga cuidado, la salida de los scripts ( stdouty stderr) continuaría yendo a su terminal a menos que lo redirija a algún lugar), y luego waitpara ello:

really_long_script.sh &
dosomethingelse
wait; echo Finished

Si ya ha ejecutado el script, puede suspenderlo Ctrl-Zy luego ejecutar algo como:

fg ; echo Finished

Donde fgpone el proceso suspendido en primer plano ( bglo haría funcionar en segundo plano, más o menos como comenzó &)

una tierra
fuente
@mlissner Extendió mi respuesta para cubrir este caso
aland
8
Como fgdevuelve el valor del trabajo que reanuda, puede usarlo fg && echo Finishedsi desea asegurarse de que el comando tenga éxito antes de ejecutar el otro comando.
palswim
13

También puedes usar el control de trabajo de bash. Si empezaste

$ really_long_script.sh

luego presione ctrl + z para suspenderlo:

^Z
[1]+  Stopped                 really_long_script.sh
$ bg

para reiniciar el trabajo en segundo plano (como si lo hubiera iniciado really_long_script.sh &). Entonces puedes esperar este trabajo de fondo con

$ wait N && echo "Successfully completed"

donde N es la ID del trabajo (probablemente 1 si no ejecutó ningún otro trabajo en segundo plano) que también se muestra como se muestra [1]arriba.

Jaap Eldering
fuente
11

Si el proceso no se ejecuta en tty actual, intente esto:

watch -g ps -opid -p <pid>; mycommand
Marinos An
fuente
2
Me encanta el uso creativo de watch. Respuesta súper corta y uno aprende una nueva forma útil de usar el reloj
Piotr Czapla
2

Resulta que esto no es tan difícil: simplemente puede escribir el siguiente comando en la ventana mientras se ejecuta el existente, presionar enter y, cuando finalice el primero, el segundo comando se ejecutará automáticamente.

Supongo que hay formas más elegantes, pero esto parece funcionar.

Edición para agregar que si desea ejecutar una alerta después de que finalice el comando, puede crear estos alias en .bashrc, luego ejecutar alertmientras se está ejecutando:

 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
fuente
77
A menos que el script en ejecución espere una entrada en un punto.
Daniel Beck
@Daniel - sí ... eso es un problema. Disparar.
mlissner
1

Hace un tiempo escribí un guión para esperar el final de otro proceso. NOISE_CMDpodría ser algo así notify-send ..., dado que DISPLAYestá configurado correctamente.

#!/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

Sin ningún argumento, solo haga un poco de ruido de inmediato. Este comportamiento permite algo como esto por conveniencia (digamos que llama script a continuación alarm.sh):

apt-get upgrade ; ~/bin/alarm.sh

Por supuesto, puede hacer muchas cosas divertidas con un script de este tipo, como dejar que una instancia alarm.shespere una instancia de alarm.sh, que esté esperando algún otro comando. O ejecutar un comando justo después de que una tarea de otro usuario conectado haya terminado ...>: D

Una versión anterior de la secuencia de comandos anterior puede ser interesante, si desea evitar una dependencia pgrepy aceptar buscar ID de proceso usted mismo:

#!/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 "$@"

Ligeramente fuera de tema, pero útil en situaciones similares: uno podría estar interesado en reptyruna herramienta que "robe" un proceso de algún shell (padre) y lo ejecute desde el shell actual. He intentado implementaciones similares y reptyres la más bonita y confiable para mis propósitos.

Piscoris
fuente