Execute um comando bash quando um processo for concluído

Execute um comando bash quando um processo for concluído

Tenho dois scripts que usam GPU e treinam modelos de ML. Quero iniciá-los antes de dormir, para que funcionem à noite e espero ver alguns resultados pela manhã.

Mas como a memória da GPU é limitada, quero executá-los em série em vez de paralelo.

Eu posso fazer isso com python train_v1.py && python train_v2.py; mas digamos que comecei a treinar o train_v1. Nesse ínterim, como o treinamento é muito demorado, iniciei e terminei a implementação do segundo script, train_v2.pye quero executá-lo automaticamente quando python train_v1.pyterminar.

Como posso conseguir isso? Obrigado.

Responder1

Aqui está uma abordagem que não envolve fazer loop e verificar se o outro processo ainda está ativo ou chamar train_v1.pyde uma maneira diferente do que você faria normalmente:

$ python train_v1.py
^Z
[1]+  Stopped                 python train_v1.py
$ % && python train_v2.py

Sou ^Zeu pressionando Ctrl+ Zenquanto o processo está em hibernação, train_v1.pyenviando um SIGTSTPsinal. Então, digo ao shell para ativá-lo com %, usando-o como um comando ao qual posso adicionar o && python train_v2.pyno final. Isso faz com que ele se comporte como se você tivesse feito isso python train_v1.py && python train_v2.pydesde o início.

Em vez de %, você também pode usar fg. É a mesma coisa. Se quiser saber mais sobre esses tipos de recursos do shell, você pode ler sobre eles ema seção "JOB CONTROL" da página de manual do bash.

EDIT: Como continuar adicionando itens à fila

Conforme apontado por jamesdlin em um comentário, se você tentar continuar o padrão a ser adicionado, train_v3.pypor exemplo, antes do início da v2, descobrirá que não pode:

$ % && python train_v2.py
^Z
[1]+  Stopped                 python train_v1.py

train_v1.pyfica parado porque train_v2.pyainda não começou, e você não pode parar/suspender/adormir algo que nem começou.

$ % && python train_v3.py

resultaria no mesmo que

python train_v1.py && python train_v3.py

porque %corresponde ao último processo suspenso. Em vez de tentar adicionar v3assim, deve-se usar o histórico:

$ !! && python train_v3.py
% && python train_v2.py && python train_v3.py

Pode-se fazer a expansão do histórico como acima, ou recuperar o último comando com um atalho de teclado (como up) e adicionar v3 ao final.

$ % && python train_v2.py && python train_v3.py

Isso é algo que pode ser repetido para adicionar mais ao pipeline.

$ !! && python train_v3.py
% && python train_v2.py && python train_v3.py
^Z
[1]+  Stopped                 python train_v1.py
$ !! && python train_v4.py
% && python train_v2.py && python train_v3.py && python train_v4.py

Responder2

Se você já iniciou python train_v1.py, você poderia usar pgreppara pesquisar esse processo até que ele desapareça e, em seguida, executar seu segundo script Python:

while pgrep -u "$USER" -fx 'python train_v1.py' >/dev/null
do
    # sleep for a minute
    sleep 60
done
python train_v2.py

Ao usar -fe -xvocê compara a linha de comando exata que foi usada para iniciar o primeiro script Python. Em alguns sistemas, pgrepimplementa uma -qopção que o torna silencioso (assim como grep -q), o que significa que o redirecionamento para /dev/nullnão seria necessário.

A -uopção restringe a correspondência aos comandos que você está executando (e não a um amigo ou outra pessoa no mesmo sistema).

Se você ainda não iniciou o primeiro script:

Conforme mencionado nos comentários, você poderia simplesmente iniciar o segundo script logo após o primeiro script. O fato de o segundo script não existir ou ainda não estar pronto para ser executado não importa (desde que esteja pronto para ser executado quando o primeiro script terminar):

python train_v1.py; python train_v2.py

Fazer dessa forma iniciará o segundo script, independentemente do status de saída do primeiro script. Usar &&em vez de ;, como você mostra na pergunta, também funcionará, mas exigirá que o primeiro script seja concluído com êxito para que o segundo script seja iniciado.

Responder3

Você pode iniciar o primeiro script com

python train_v1.py; touch finished

Depois basta fazer um loop que verifica regularmente se finishedexiste:

while [ ! -f finished ] ; do     
    sleep 5
done
python train_v2.py
rm finished

Responder4

Se você não precisa saber o status de saída do primeiro script, recomendo algo comoo que Kusalananda escreveu.

Se você precisar saber o status de saída (o que provavelmente não é necessário neste caso, mas outra pessoa pode aparecer procurando uma solução que faça isso), é mais complicado. Eu escrevi umpequeno utilitário Linuxpwaitque permite aguardar a conclusão de um processo e descobrir seu status de saída.

informação relacionada