Ctrl-C com dois comandos simultâneos no bash

15

Eu quero executar dois comandos simultaneamente no bash em uma máquina Linux. Portanto, no meu ./execute.shscript bash eu coloquei:

command 1 & command 2
echo "done"

No entanto, quando eu quero parar o script bash e pressionar Ctrl+ C, apenas o segundo comando é parado. O primeiro comando continua em execução. Como garantir que o script completo do bash esteja parado? Ou, de qualquer forma, como faço para parar os dois comandos? Porque, neste caso, não importa quantas vezes eu pressione Ctrl+, Co comando continua em execução e sou forçado a fechar o terminal.

maero21
fonte
Transforme a pergunta de acompanhamento em uma pergunta separada e remova aqui. Agora, não está claro para o visitante se a pergunta foi respondida ou apenas a pergunta inicial.
Zelda

Respostas:

17

Se você digitar

command 1 & command 2

isso é igual a

command 1 &
command 2

isto é, ele executará o primeiro comando em segundo plano e, em seguida, executará o segundo comando em primeiro plano. Especialmente, isso significa que você echo "done"é impresso após o command 2término, mesmo que command 1ainda esteja em execução.

Você provavelmente quer

command 1 &
command 2 &
wait
echo "done"

Isso executará os dois comandos em segundo plano e aguardará a conclusão de ambos.


Se você pressionar CTRL-C, isso enviará apenas o sinal SIGINT para o processo em primeiro plano, ou seja, command 2na sua versão ou waitna minha versão.

Eu sugeriria definir uma armadilha como esta:

#!/bin/bash

trap killgroup SIGINT

killgroup(){
  echo killing...
  kill 0
}

loop(){
  echo $1
  sleep $1
  loop $1
}

loop 1 &
loop 2 &
wait

Com a interceptação, o sinal SIGINT produzido pelo CTRL-C é interceptado e substituído pela killgroupfunção, que mata todos esses processos.

michas
fonte
Obrigado pela sua resposta! No entanto, tenho uma pergunta de acompanhamento. Agora tenho o seguinte script: trap killgroup SIGINT killgroup () {echo kill ... kill 0} command001 command1 & command2 Eu omiti o comando wait porque o script pode continuar quando o comando2 for concluído. No entanto, parece que o comando 1 já inicia quando executo o script. Posso consertar isso? Graças
maero21
command1 inicia diretamente depois que command001 é concluído. você pode usar set -xno início do seu script para imprimir os comandos que são executados.
Michas
6

Ao colocar um comando em segundo plano a partir de um script, o PID não é exibido na tela. Você pode usar a variável interna $!que armazena o PID do último processo para capturar o PID do comando1.

command1 &
echo $!

ecoaria o PID do command1.

O Bash também fornece a armadilha integrada que você pode usar para registrar uma sequência de comandos para executar quando sinais específicos são recebidos. Você pode usar isso para capturar o SIGINT e matar o command1 antes de sair do script principal, por exemplo

#!/bin/bash

onINT() {
echo "Killing command1 $command1PID too"
kill -INT "$command1PID"
exit
}

trap "onINT" SIGINT
command1 &
command1PID="$!"
comamnd2
echo Done

Agora, enquanto o comando 2 estiver em execução, pressionar Ctrl Cfará com que o comando1 e o comando2 sejam enviados ao SIGINT.

Comunidade
fonte
Você também pode usar a %nsintaxe para eliminar trabalhos específicos em segundo plano neste shell. Normalmente, você decide kill %1matar o último e o único processo em segundo plano. Com mais processos em segundo plano, use jobspara ver a lista primeiro.
9000
@ 9000: Sim, mas o OP está executando seus comandos em um script (./execute.sh), portanto, conseguir empregos para trabalhar é muito mais problemático, com certeza?
@ lain: certamente. Perdi o ponto sobre a execução de um script.
9000
5

As versões mais recentes do GNU Parallel farão o que você deseja:

parallel ::: "command 1" "command 2"
echo "done"

Ou se commandpermanecer o mesmo:

parallel command ::: 1 2
echo done

Assista ao vídeo de introdução para uma rápida introdução: https://www.youtube.com/playlist?list=PL284C9FF2488BC6D1

Percorra o tutorial (man paralelo_tutorial). Você comanda a linha com amor por isso.

Ole Tange
fonte
1

Ctrl+ Cenvia um sinal SIGINT para o seu processo frontal, que é command2. command1é executado em segundo plano, portanto, não está preocupado com o fluxo de entrada.

Quando você digita command1 &, o bash deve fornecer o PID do processo, algo assim [1234]. Para matar esse processo, você pode usar kill 1234. Agora, se você tiver os PIDs de ambos command1e command2(dê uma olhada ps -ef), poderá usar killpara finalizar todos eles:

kill pid1 pid2 pid3 ...

Um pequeno truque seria executar os dois comandos em segundo plano, com:

command1 & command2 &

O Bash oferece dois PIDs, prontos para serem mortos. Outro truque seria trazer de volta command1em primeiro plano depois que você matasse command2:

command1 & command2
# Hit Ctrl+C : command2 terminates.

fg # Bring back command1 to foreground.
# Hit Ctrl+C again, command1 terminates.

Mais informações disponíveis aqui: http://linuxg.net/how-to-manage-background-and-foreground-processes/

John WH Smith
fonte