Seu killcomando está ao contrário.
Como muitos comandos UNIX, as opções que começam com menos devem vir primeiro, antes de outros argumentos.
Se você escrever
kill -INT 0
vê -INTcomo uma opção e envia SIGINTpara 0( 0é um número especial que significa todos os processos no grupo de processos atual).
Mas se você escrever
kill 0 -INT
ele vê o 0, decide que não há mais opções, então usa SIGTERMpor padrão. E envia isso para o grupo de processos atual, o mesmo que se você fizesse
kill -TERM 0 -INT
(ele também iria tentar enviar SIGTERMpara -INT, o que causaria um erro de sintaxe, mas ele envia SIGTERMa 0primeira, e nunca fica tão longe.)
Portanto, seu script principal está recebendo um SIGTERMantes de executar o waite echo DONE.
Adicionar
trap 'echo got SIGTERM' TERM
no topo, logo após
trap 'killall' INT
e execute-o novamente para provar isso.
Como Stephane Chazelas aponta, seus filhos em segundo plano ( process1, etc.) ignoram SIGINTpor padrão.
De qualquer forma, acho que enviar SIGTERMfaria mais sentido.
Finalmente, não tenho certeza se kill -process groupé garantido ir primeiro às crianças. Ignorar sinais durante o desligamento pode ser uma boa ideia.
Então tente o seguinte:
#!/bin/bash
trap 'killall' INT
killall() {
trap '' INT TERM # ignore INT and TERM while shutting down
echo "**** Shutting down... ****" # added double quotes
kill -TERM 0 # fixed order, send TERM not INT
wait
echo DONE
}
./process1 &
./process2 &
./process3 &
cat # wait forever