Como parar um script dentro de outro script

5

Estou executando o Ubuntu 16.04, usando um emulador de terminal.

Eu tenho dois scripts: a.sh e b.sh . a.sh chamadas b.sh .

a.sh

echo "a is running"
sleep 5s
bash b.sh
sleep 5s
echo "a is still running"

b.sh

echo "b is now running"
sleep 1000s
echo "Bananas"

Existe uma maneira de executar a.sh , parar b.sh antes de ser concluído (antes de imprimir Bananas) e deixar a.sh executar até a conclusão e imprimir "a ainda está em execução"?

Eu tentei colocar pkill -f b.sh dentro de a.sh , mas os dois a.sh e b.sh pararam antes de a.sh concluir.

    
por speld_rwong 03.02.2018 / 21:54

4 respostas

4

Em um ponto, você estava perto do que parece querer.

a.sh

#!/bin/sh

echo "a.sh running"
sleep 10s
(sleep 10s; pkill -f b.sh) &
bash b.sh
sleep 10s
pkill -f b.sh
echo "a.sh still running"

b.sh (igual à sua versão)

echo "b is now running"
sleep 1000s
echo "Bananas"

A linha (sleep 10s; pkill -f b.sh) & em a.sh cria um subshell que dorme por dez segundos e depois mata b.sh . Em seguida, coloca essa subcama no fundo então a.sh pode continuar em execução. a.sh , em seguida, executa b.sh , que executa sleep 1000s . Dez segundos depois, o sleep 10s no subshell termina, e ele (o subshell) executa pkill -f b.sh , matando o processo b.sh . a.sh , em seguida, continua em execução. O segundo pkill -f b.sh não faz nada, já que o processo b.sh já foi finalizado.

Isso é o que acontece quando é executado:

$ ./a.sh
a.sh running                                              # immediately
b is now running                                          # about 10 seconds after the above
./a.sh: line 6: 13684 Terminated            bash b.sh     # about 10 seconds after the above
a.sh still running                                        # about 10 seconds after the above
$                                                         # immediately after the above

Isso tem a vantagem de que a.sh pode voltar a ser executado imediatamente se b.sh terminar rapidamente. Somente se b.sh for executado por mais de 10 segundos, o pkill será acionado. Na outra resposta, a.sh tem que ficar ocioso enquanto o sleep antes do pkill ocupar o tempo, mesmo que b.sh já tenha terminado.

Outro usuário postado um comentário (agora excluído) dizendo

I get

./a.sh
a.sh running
b is now running
Terminated

not what you report. Any idea?

Foi uma noite escura e tempestuosa. De repente, um tiro soou!

- Era um escuro e Noite tempestuosa, Snoopy por Charles M. Schulz

O argumento para pgrep e pkill é uma expressão regular; especificamente, uma expressão regular estendida 1 . Tente executar cat e cut simultaneamente 2 . Então, faça pgrep c.t - ele listará dois PIDs, e você pode confirmar com ps que eles são cat e cut .

Se você tivesse feito exatamente o que eu disse, você realmente deveria ter obtido os mesmos resultados que eu fiz. Mas estou disposto a apostar 42 Zorkmids que você fez algo diferente. Você também:

  1. Começou a.sh com #!/bin/bash como uma she-bang (primeira linha) em vez de #!/bin/sh ou
  2. Ran a.sh com bash a.sh em vez de ./a.sh .

De repente, um tiro soou!

b.sh é uma expressão regular que corresponde a b seguido por qualquer caractere seguido por sh . Corresponde a si mesmo ( b.sh ), e também coisas como b!sh , b~sh , b&sh , b7sh e bush . E…… (espere)…… também corresponde a bash !

Então pkill -f b.sh mata todos os processos bash, e também qualquer outro que tenha b.sh em seus nomes. Quando eu corri o que eu corri, meu shell de login (que é pelo menos parcialmente imune a pkill ) foi bash, mas a.sh estava sendo executado com /bin/sh (mas b.sh estava sendo executado em bash ). O comando pkill -f b.sh eliminou o processo bash b.sh porque continha ambos bash e b.sh . Mas o /bin/sh que estava executando a.sh não foi afetado.

Assumindo que você o executou da maneira que acredito que você o executou (uma das duas alternativas), todos os processos de script estavam executando bash . E assim, o comando pkill -f b.sh matou todos os processos de script.

Alterar pkill -f b.sh para pkill -f 'b\.sh' (escapando do ponto para que ele corresponda apenas a um ponto, e não a qualquer caractere), e você deve obter os resultados corretos.

por 04.02.2018 / 04:26
2

A linha:

bash b.sh

aguardará a conclusão do script b.sh , não é possível fazer nada no script antes que b.sh termine porque o a.sh não tem o controle.

Para que o script b.sh vá para o segundo plano e devolva o controle a a.sh , use um semelhante a:

bash b.sh &

Isso retorna o controle para a.sh imediatamente e podemos ter esse tipo de script:

#!/bin/bash 

echo "a is running"
sleep 1s
bash b.sh &
sleep 2
pkill -f 'b\.sh'
sleep 1s
echo "a is still running"

Enquanto o script b.sh continua sendo:

#!/bin/bash
echo "b is now running"
sleep 1000s
echo "Bananas"

Na execução, você receberá:

$ ./a.sh
a is running
b is now running
./a.sh: line 7:  8424 Terminated              bash b.sh
a is still running

Para evitar a "Mensagem de controle do trabalho", use um sub-shell, altere bash b.sh & para:

(bash b.sh &)

E, em execução, você receberá:

$ ./a.sh
a is running
b is now running
a is still running

Editar:

O problema com uma resposta 'Terminada' que você relata é que o comando pkill que você postou pkill -f b.sh está usando o argumento como regex. Essa correspondência de regex b seguida por qualquer caractere . seguido por sh . Que correspondem ao nome 'bash' que é o programa que começou a correr também a.sh , que explica porque o a.sh está a ser terminado. Altere o comando pkill para:

pkill -f 'b\.sh'

para garantir que o regex correspondido seja uma string simples 'b.sh'.

Você pode ver os processos que o pkill corresponderia colocando isso:

ps -f -o pid,cmd,args |grep 'b.sh'

pouco antes do pkill.

Altere para grep 'b\.sh' para descobrir que ele realmente corresponde a apenas um pid.

    
por 04.02.2018 / 01:16
0

Seu script a.sh deve executar o script b.sh como um trabalho em segundo plano para poder rescindi-lo prematuramente.

Como b.sh é executado como um job em background, você tem acesso a seu ID de processo (PID), o que significa que você pode usar kill para sinalizá-lo. Isso é mais seguro do que usar pkill , o que potencialmente sinalizará outros processos que correspondem ao padrão que você está dando a ele.

Você pode fazer o seguinte em a.sh :

#!/bin/sh

echo "A: I'm alive!"
./b.sh & bpid=$!

echo 'A: Sleeping for a short while'
sleep 2

echo 'A: Signalling B'
kill "$bpid"

echo "A: I'm done here"

O $! será expandido para o PID da tarefa em segundo plano iniciada mais recentemente.

Exemplo de b.sh :

#!/bin/sh

echo "B: I'm alive!"

echo 'B: Sleeping for longer than A does'
sleep 10

echo "B: I'm done here"

Executando:

$ ./a.sh
A: I'm alive!
A: Sleeping for a short while
B: I'm alive!
B: Sleeping for longer than A does
A: Signalling B
A: I'm done here

Observe que b.sh pode não ser encerrado até que a chamada sleep seja concluída. Este é o caso de bash , dash , zsh e pdksh , mas não com ksh93 (com o qual b.sh termina imediatamente).

    
por 05.02.2018 / 11:27
-2

Eu usei o método abaixo. Adicionado uma linha extra em b.sh, que faz com que o trabalho seja conforme o requisito

script a.sh será o mesmo

script a.sh

echo "a is running"
sleep 5s
bash b.sh
sleep 5s
echo "a is still running"

=============================================== ======

script b.sh

echo "b is now running"
sleep 20s

exit
O comando

== > exit sairá do script

echo "Bananas"

saída

   a is running
b is now running
a is still running
    
por 04.02.2018 / 10:02