Deixe-me começar dizendo, você poderia apenas in-line todas as coisas que você tem em scannew
, já que você está wait
ing, a menos que você pretenda digitalizar novamente em algum outro ponto no seu script. É realmente a chamada para wc
que você está preocupado pode levar muito tempo, o que, se acontecer, você pode terminá-lo. Esta é uma maneira simples de configurar isso usando trap
, que permite capturar sinais enviados para um processo e definir seu próprio manipulador para ele:
#! /usr/bin/env bash
# print a line just before we run our subshell, so we know when that happens
printf "Lets do something foolish...\n"
# trap SIGINT since it will be sent to the entire process group and we only
# want the subshell killed
trap "" SIGINT
# run something that takes ages to complete
BAD_IDEA=$( trap "exit 1" SIGINT; ls -laR / )
# remove the trap because we might want to actually terminate the script
# after this point
trap - SIGINT
# if the script gets here, we know only 'ls' got killed
printf "Got here! Only 'ls' got killed.\n"
exit 0
No entanto, se você quiser manter a maneira como faz as coisas, com scannew
sendo uma função executada como um trabalho em segundo plano, é necessário um pouco mais de trabalho.
Como você deseja a entrada do usuário, a maneira correta de fazer isso é usar read
, mas ainda precisamos que o script continue se scannew
for concluído e não apenas aguardar a entrada do usuário para sempre. read
torna isso um pouco complicado, porque bash
espera que o comando atual seja concluído antes de permitir que trap
s funcione nos sinais. A única solução para isso que eu conheço, sem refatorar o script inteiro, é colocar read
em um loop while true
e dar a ele um tempo limite de 1 segundo, usando read -t 1
. Dessa forma, sempre levará pelo menos um segundo para o processo ser concluído, mas isso pode ser aceitável em uma situação como a sua, na qual você deseja essencialmente executar um daemon de pesquisa que liste dispositivos usb.
#! /usr/bin/env bash
function slow_background_work {
# condition can be anything of course
# for testing purposes, we're just checking if the variable has anything in it
while [[ -z $BAD_IDEA ]]
do
BAD_IDEA=$( ls -laR / 2>&1 | wc )
done
# '$$' normally gives us our own PID
# but in a subshell, it is inherited and thus
# gives the parent's PID
printf "\nI'm done!\n"
kill -s SIGUSR1 -- $$
return 0
}
# trap SIGUSR1, which we're expecting from the background job
# once it's done with the work we gave it
trap "break" SIGUSR1
slow_background_work &
while true
do
# rewinding the line with printf instead of the prompt string because
# read doesn't understand backslash escapes in the prompt string
printf "\r"
# must check return value instead of the variable
# because a return value of 0 always means there was
# input of _some_ sort, including <enter> and <space>
# otherwise, it's really tricky to test the empty variable
# since read apparently defines it even if it doesn't get input
read -st1 -n1 -p "prompt: " useless_variable && {
printf "Keypress! Quick, kill the background job w/ fire!\n"
# make sure we don't die as we kill our only child
trap "" SIGINT
kill -s SIGINT -- "$!"
trap - SIGINT
break
}
done
trap - SIGUSR1
printf "Welcome to the start of the rest of your script.\n"
exit 0
Claro, se o que você realmente quer é um daemon que esteja atento a mudanças no número de dispositivos usb ou algo assim, você deve procurar em systemd
, que pode fornecer algo mais elegante.