O problema que você tem é causado pelo subshell gerado quando você chama transprog
para executar em segundo plano (o que o símbolo &
faz). Você tem que entender que o seu script bash não é um programa, mas um shell script que se comporta de acordo com as regras do shell - um shell só pode executar um processo de cada vez e esperar que ele termine.
O " sistema de empregos " permite que você execute outro processo e não o espere, mas quando esse processo é uma função definida no bash, o bash gerará um "sub shell" que " copia " todas as variáveis e funções do original shell e acompanhe isso como um trabalho. O shell copiado não está ciente de que as alterações fizeram dele o shell pai - porque ele não está referenciando as variáveis do shell pai diretamente - apenas uma cópia delas.
O que você precisa fazer para seu loop de plano de fundo é usar um mecanismo Inter Process Communication ( IPC ) para permitir que o shell pai notifique o loop no subshell para abortar. Para uma sinalização tão simples, geralmente um "sinal POSIX" é apropriado.
O sinal mais apropriado para usar é o "sinal de interrupção", SIGINT (número de sinal 2), que é produzido quando você pressiona CTRL + C e normalmente interrompe seu script. Mas podemos capturar este sinal e atualizar uma variante local usando o comando interno trap
.
Aqui está um exemplo simples:
#!/bin/bash
function test() {
var=0
trap 'var=1' 2
while [ "$var" = 0 ]; do
sleep 3
echo 'still sleeping'
done
echo "done sleeping"
}
test &
pid=$!
echo $pid;
sleep 5
echo "Killing $pid"
kill -2 "$pid"
wait
Isso produzirá:
$ ./test.sh
8359
still sleeping
Killing 8359
still sleeping
done sleeping
O que acontece aqui é que após 5 segundos o loop completou uma rodada e agora está dormindo na segunda rodada quando o pai usa o kill
embutido para sinalizar a subshell. O sono do subshell é interrompido, ele recebe o sinal e atualiza a variável, depois retoma o sono Quando o loop desperta, ele envia o texto, retorna e vê que o var foi alterado e sai do loop.
Eu uso o wait
embutido no pai apenas para garantir que o processo pai não saia antes do subshell. Essencialmente, "aguarda" que a sub-rede termine.
[UPDATE]
Depois de olhar o OP um pouco mais, há mais alguns subshells acontecendo, e eu não quero lutar contra isso - subshells (além de funções) é uma boa maneira de gerenciar a complexidade em um script - mas isso faz com que você encontre o PID correto para enviar o sinal para um bit difícil.
Em vez disso, podemos usar outro mecanismo de IPC chamado "segmento de memória compartilhada". Isso permite que um processo aloque um lote de memória, atribua um nome a ele e permita que vários processos acessem a mesma memória (ela é abordada em detalhes no documento do IPC vinculado acima). Enquanto no Bash não podemos abordar diretamente esse recurso (ao contrário do que um programa C, por exemplo, pode fazer), podemos usar um mecanismo do Linux que expõe o IPC de memória compartilhada a qualquer programa através da categoria de dispositivo /dev/shm
. Então, o script OP pode ser algo assim:
#!/bin/bash
transtest () {
local sync="$1"
i2="Im Working On It!"
var=0
(
while [ -z "$(cat $sync)" ]; do sleep 1; done
echo "# Your files are DONE TRANSFERING!"
sleep 1
echo "100"
) | zenity --progress --title="File Transfer In Progress" --text="# Im Getting Warmed Up" \
--percentage=0 --pulsate --auto-close --auto-kill
}
sync=/dev/shm/syntest-$$ # allocate a shared memory segment name for this process
echo '' > $sync # init the shm
transtest $sync &
sleep 20
echo "Signaling done"
echo "done" > $sync
echo "waiting"
wait
# remove the shm segment. While not actually necessary (it will clear
# when you reboot), I like to keep a clean system
rm $sync
exit
Basicamente, tratamos o segmento SHM como um arquivo e apenas armazenamos alguns dados nele. O loop continua lendo o "arquivo" e quando há conteúdo lá, ele se rompe.
Uma questão permanece: por que não apenas alocar um arquivo temporário (usando mktemp
) e usá-lo para sincronização? Bem - isso funcionaria quase o mesmo! A principal diferença, e porque eu prefiro SHM para esse tipo de coisa, é que não é realmente um arquivo e não está sujeito as operações de E / S do sistema de arquivos, erros de disco e os gostos, e se eu não conseguir limpá-lo, ele ir embora na próxima vez que o sistema inicializar.