Como executar um comando após o final de um já existente?

32

Se eu iniciar um script que demorará muito tempo, inevitavelmente o percebo depois que o iniciei e gostaria de ter uma maneira de fazer algum tipo de alerta quando ele terminar.

Então, por exemplo, se eu executar:

really_long_script.sh

e pressione enter ... como posso executar outro comando quando terminar?

mlissner
fonte

Respostas:

45

Você pode separar vários comandos ;, para que sejam executados sequencialmente, por exemplo:

really_long_script.sh ; echo Finished

Se você deseja executar o próximo programa apenas se o script terminar com o código de retorno 0 (o que geralmente significa que foi executado corretamente), então:

really_long_script.sh && echo OK

Se você quiser o contrário (ou seja, continue apenas se o comando atual falhar), então:

really_long_script.sh || echo FAILED

Você pode executar seu script em segundo plano (mas cuidado, a saída de scripts ( stdoute stderr) continuaria indo para o seu terminal, a menos que você o redirecionasse para algum lugar), e depois waitpara ele:

really_long_script.sh &
dosomethingelse
wait; echo Finished

Se você já executou o script, poderá suspendê-lo Ctrl-Ze executar algo como:

fg ; echo Finished

Onde fgcoloca o processo suspenso em primeiro plano ( bgfaria com que fosse executado em segundo plano, praticamente como no começo &)

uma terra
fonte
@mlissner Expandi minha resposta para cobrir este caso
aland 11/11/11
8
Como fgretorna o valor do trabalho que ele continua, você pode usar fg && echo Finishedse desejar garantir que o comando seja bem-sucedido antes de executar o outro comando.
palswim
13

Você também pode usar o controle de tarefas do bash. Se você começou

$ really_long_script.sh

depois pressione ctrl + z para suspendê-lo:

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

para reiniciar o trabalho em segundo plano (como se o tivesse iniciado really_long_script.sh &). Em seguida, você pode esperar pelo trabalho em segundo plano com

$ wait N && echo "Successfully completed"

onde N é o ID do trabalho (provavelmente 1 se você não executou outros trabalhos em segundo plano), que também é exibido como [1]acima.

Jaap Eldering
fonte
11

Se o processo não for executado no tty atual, tente o seguinte:

watch -g ps -opid -p <pid>; mycommand
Marinos An
fonte
2
Eu amo o uso criativo de watch. Resposta super curta e aprende-se uma nova maneira útil de usar o relógio
Piotr Czapla
2

Acontece que isso não é tão difícil: você pode simplesmente digitar o próximo comando na janela enquanto o existente é executado, pressionar enter e, quando o primeiro terminar, o segundo comando será executado automaticamente.

Acho que existem maneiras mais elegantes, mas isso parece funcionar.

Edição para adicionar que, se você quiser executar um alerta após a conclusão do comando, poderá criar esses aliases em .bashrc e executar alertenquanto estiver em execução:

 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
fonte
7
A menos que o script em execução espere entrada em um ponto.
Daniel Beck
@ Daniel - sim ... isso é um problema. Tiro.
mlissner
1

Há um tempo atrás, escrevi um script para aguardar o final de outro processo. NOISE_CMDpode ser algo como notify-send ..., dado que DISPLAYestá definido corretamente.

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

Sem qualquer argumento, basta fazer barulho imediatamente. Esse comportamento permite algo assim por conveniência (digamos que você chame o script abaixo alarm.sh):

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

É claro que você pode fazer muitas coisas engraçadas com esse script, como deixar uma instância de alarm.shespera por uma instância de alarm.sh, que está aguardando algum outro comando. Ou executando um comando logo após a conclusão de alguma tarefa de outro usuário conectado ...>: D

Uma versão anterior do script acima pode ser interessante, se você quiser evitar uma dependência pgrepe aceitar os IDs do processo de pesquisa:

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

Ligeiramente fora de tópico, mas útil em situações semelhantes: reptyralguém pode estar interessado em uma ferramenta que "rouba" um processo de algum shell (pai) e o executa no shell atual. Tentei implementações semelhantes e reptyré a mais bonita e mais confiável para meus propósitos.

Piscoris
fonte