Respostas:
Para limpar alguma bagunça, trap
pode ser usado. Pode fornecer uma lista de coisas executadas quando um sinal específico chega:
trap "echo hello" SIGINT
mas também pode ser usado para executar algo se o shell sair:
trap "killall background" EXIT
É um builtin, portanto help trap
, você fornecerá informações (funciona com o bash). Se você quiser apenas matar trabalhos em segundo plano, poderá fazer
trap 'kill $(jobs -p)' EXIT
Cuidado para usar o single '
, para impedir que o shell substitua o $()
imediatamente.
kill $(jobs -p)
não funciona no traço, porque ele executa a substituição de comando em um subshell (ver Command Substituição no homem traço)
killall background
suposto ser um espaço reservado? background
não está na página de manual ...
Isso funciona para mim (aprimorado graças aos comentaristas):
trap "trap - SIGTERM && kill -- -$$" SIGINT SIGTERM EXIT
4.3.30(1)-release
OSX e também está confirmado no Ubuntu . Há uma wokaround obvoius , embora :)
-$$
. Ele avalia como '- <PID> `por exemplo -1234
. Na página de manual kill // página de manual interna, um traço inicial especifica o sinal a ser enviado. No entanto - provavelmente bloqueia isso, mas o traço principal não está documentado de outra forma. Qualquer ajuda?
man 2 kill
, que explica que quando um PID é negativo, o sinal é enviado para todos os processos no grupo de processos com o ID fornecido ( en.wikipedia.org/wiki/Process_group ). É confuso que isso não seja mencionado em man 1 kill
ou man bash
e possa ser considerado um bug na documentação.
Atualização: https://stackoverflow.com/a/53714583/302079 aprimora isso adicionando o status de saída e uma função de limpeza.
trap "exit" INT TERM
trap "kill 0" EXIT
Por que converter INT
e TERM
sair? Porque ambos devem acionar o kill 0
sem inserir um loop infinito.
Por gatilho kill 0
on EXIT
? Como saídas de script normais devem acionarkill 0
.
Por que kill 0
? Porque subcascas aninhadas precisam ser mortas também. Isso derrubará toda a árvore do processo .
kill 0
significa / faz?
trap 'kill $ (jobs -p)' EXIT
Eu faria apenas pequenas alterações na resposta de Johannes e usaria jobs -pr para limitar a interrupção dos processos em execução e adicionar mais alguns sinais à lista:
trap 'kill $(jobs -pr)' SIGINT SIGTERM EXIT
A trap 'kill 0' SIGINT SIGTERM EXIT
solução descrita na resposta do @ tokland é muito boa, mas o Bash mais recente falha com uma falha de segmantação ao usá-lo. Isso porque o Bash, a partir da versão 4.3, permite a recursão de interceptação, que se torna infinita neste caso:
SIGINT
ou SIGTERM
ou EXIT
;kill 0
, que envia SIGTERM
para todos os processos do grupo, incluindo o próprio shell;Para solucionar isso, cancele o registro manual da armadilha:
trap 'trap - SIGTERM && kill 0' SIGINT SIGTERM EXIT
A maneira mais elegante, que permite imprimir o sinal recebido e evita mensagens "Terminated:":
#!/usr/bin/env bash
trap_with_arg() { # from https://stackoverflow.com/a/2183063/804678
local func="$1"; shift
for sig in "$@"; do
trap "$func $sig" "$sig"
done
}
stop() {
trap - SIGINT EXIT
printf '\n%s\n' "recieved $1, killing children"
kill -s SIGINT 0
}
trap_with_arg 'stop' EXIT SIGINT SIGTERM SIGHUP
{ i=0; while (( ++i )); do sleep 0.5 && echo "a: $i"; done } &
{ i=0; while (( ++i )); do sleep 0.6 && echo "b: $i"; done } &
while true; do read; done
UPD : exemplo mínimo adicionado; stop
Função aprimorada para evitar a captura de sinais desnecessários e ocultar as mensagens "Terminadas:" da saída. obrigado Trevor Boyd Smith pelas sugestões!
stop()
que você fornecer o primeiro argumento como o número de sinal, mas depois você codificar o que sinais estão sendo registro cancelado. em vez de codificar os sinais que estão sendo cancelados o registro, você pode usar o primeiro argumento para cancelar o registro na stop()
função (isso poderia interromper outros sinais recursivos (exceto os 3 codificados permanentemente)).
SIGINT
, mas kill 0
envia SIGTERM
, que ficará preso novamente. Isso não produzirá recursão infinita, porém, porque SIGTERM
será retido durante a segunda stop
chamada.
trap - $1 && kill -s $1 0
deve funcionar melhor. Vou testar e atualizar esta resposta. Obrigado pela boa ideia! :)
trap - $1 && kill -s $1 0
não funcionaria também, pois não podemos matar EXIT
. Mas é realmente suficiente capturar TERM
, porque kill
envia esse sinal por padrão.
EXIT
, o trap
manipulador de sinal sempre é executado apenas uma vez.
Por segurança, acho melhor definir uma função de limpeza e chamá-la de trap:
cleanup() {
local pids=$(jobs -pr)
[ -n "$pids" ] && kill $pids
}
trap "cleanup" INT QUIT TERM EXIT [...]
ou evitando a função completamente:
trap '[ -n "$(jobs -pr)" ] && kill $(jobs -pr)' INT QUIT TERM EXIT [...]
Por quê? Porque simplesmente usando trap 'kill $(jobs -pr)' [...]
uma parte do princípio de que não vai ser trabalhos de fundo em execução quando a condição armadilha é sinalizado. Quando não há trabalhos, será exibida a seguinte mensagem (ou similar):
kill: usage: kill [-s sigspec | -n signum | -sigspec] pid | jobspec ... or kill -l [sigspec]
porque jobs -pr
está vazio - terminei nessa 'armadilha' (trocadilhos).
[ -n "$(jobs -pr)" ]
não funciona no meu bash. Eu uso o GNU bash, versão 4.2.46 (2) -release (x86_64-redhat-linux-gnu). A mensagem "kill: use" continua aparecendo.
jobs -pr
não retornar os PIDs dos filhos dos processos em segundo plano. Não destrói toda a árvore do processo, apenas apara as raízes.
Uma boa versão que funciona com Linux, BSD e MacOS X. Primeiro, tente enviar o SIGTERM e, se não tiver êxito, interrompe o processo após 10 segundos.
KillJobs() {
for job in $(jobs -p); do
kill -s SIGTERM $job > /dev/null 2>&1 || (sleep 10 && kill -9 $job > /dev/null 2>&1 &)
done
}
TrapQuit() {
# Whatever you need to clean here
KillJobs
}
trap TrapQuit EXIT
Observe que os trabalhos não incluem processos de netos.
function cleanup_func {
sleep 0.5
echo cleanup
}
trap "exit \$exit_code" INT TERM
trap "exit_code=\$?; cleanup_func; kill 0" EXIT
# exit 1
# exit 0
Como https://stackoverflow.com/a/22644006/10082476 , mas com código de saída adicionado
jobs -p não funciona em todos os shells se chamado em um sub-shell, possivelmente a menos que sua saída seja redirecionada para um arquivo, mas não para um canal. (Presumo que ele foi originalmente destinado apenas para uso interativo.)
E o que se segue:
trap 'while kill %% 2>/dev/null; do jobs > /dev/null; done' INT TERM EXIT [...]
A chamada para "jobs" é necessária com o shell do Debian, que falha ao atualizar o job atual ("%%") se ele estiver ausente.
Fiz uma adaptação da resposta do @ tokland combinada com o conhecimento de http://veithen.github.io/2014/11/16/sigterm-propagation.html quando percebi que trap
isso não é acionado se eu estiver executando um processo em primeiro plano (sem fundo &
):
#!/bin/bash
# killable-shell.sh: Kills itself and all children (the whole process group) when killed.
# Adapted from http://stackoverflow.com/a/2173421 and http://veithen.github.io/2014/11/16/sigterm-propagation.html
# Note: Does not work (and cannot work) when the shell itself is killed with SIGKILL, for then the trap is not triggered.
trap "trap - SIGTERM && echo 'Caught SIGTERM, sending SIGTERM to process group' && kill -- -$$" SIGINT SIGTERM EXIT
echo $@
"$@" &
PID=$!
wait $PID
trap - SIGINT SIGTERM EXIT
wait $PID
Exemplo disso funcionando:
$ bash killable-shell.sh sleep 100
sleep 100
^Z
[1] + 31568 suspended bash killable-shell.sh sleep 100
$ ps aux | grep "sleep"
niklas 31568 0.0 0.0 19640 1440 pts/18 T 01:30 0:00 bash killable-shell.sh sleep 100
niklas 31569 0.0 0.0 14404 616 pts/18 T 01:30 0:00 sleep 100
niklas 31605 0.0 0.0 18956 936 pts/18 S+ 01:30 0:00 grep --color=auto sleep
$ bg
[1] + 31568 continued bash killable-shell.sh sleep 100
$ kill 31568
Caught SIGTERM, sending SIGTERM to process group
[1] + 31568 terminated bash killable-shell.sh sleep 100
$ ps aux | grep "sleep"
niklas 31717 0.0 0.0 18956 936 pts/18 S+ 01:31 0:00 grep --color=auto sleep
Apenas pela diversidade, publicarei variações de https://stackoverflow.com/a/2173421/102484 , porque essa solução leva à mensagem "Terminado" no meu ambiente:
trap 'test -z "$intrap" && export intrap=1 && kill -- -$$' SIGINT SIGTERM EXIT