I need two ways to terminate a part of my bash script.Either a counter reaches a predefined number, or the user manually forces the script to continue with whatever the value the counter currently has.Specifically - I'm listing USB drives. If there is 15 of them, the function that counts them exits and the script can continue.My code looks a bit like this:scannew(){    NEW=0    OLD=$NEW    while [ true ]; do        # count the new drives        lsblk -r > drives.new        diff drives.old drives.new | grep disk | cut -d' ' -f 2 | sort > drives.all        NEW=$(wc -l drives.all | cut -d' ' -f1)        echo -en    Detected drives: $NEW    \r        sleep 0.01        if [ $NEW -eq 15 ]; then # exit if we reach the limit            break        fi    done}# SOME CODE...lsblk -r > drives.oldscannew & # start live device counter in the backgroundSCAN_PID=$! # remember it's PIDwait $SCAN_PID 2>/dev/null # wait until it diesecho It's on!# REST OF THE CODE...I tried various stuff with the read command, but the result is, the script will always wait for read to exit (after pressing ENTER) and I can't make the 15 limit condition to override that.For example I tried using read -t instead of sleep in the scannew() function:scannew(){    NEW=0    OLD=$NEW    while [ true ]; do        # count the new drives        lsblk -r > drives.new        diff drives.old drives.new | grep disk | cut -d' ' -f 2 | sort > drives.all        NEW=$(wc -l drives.all | cut -d' ' -f1)        echo -en    Detected drives: $NEW    \r        read -t 0.01 -n 1 && break # read instead of sleep        if [ $NEW -eq 15 ]; then            break        fi    done}However - it seems that the function subprocess doesn't have access to stdin, and using read -t 0.01 -n 1 < /dev/stdin && break instead didn't work either.How can I make this work?
Wait for a process to finish OR for the user to press a key
bash;shell script;function;user input
Let me start by saying, you could just inline all the stuff you have in scannew, since you're waiting anyway, unless you intend to scan again at some other point in your script. It's really the call to wc that you're concerned might take too long, which, if it does, you can just terminate it. This is a simple way to set that up using trap which allows you to capture signals sent to a process and set your own handler for it:#! /usr/bin/env bash# print a line just before we run our subshell, so we know when that happensprintf Lets do something foolish...\n# trap SIGINT since it will be sent to the entire process group and we only# want the subshell killedtrap  SIGINT# run something that takes ages to completeBAD_IDEA=$( trap exit 1 SIGINT; ls -laR / )# remove the trap because we might want to actually terminate the script# after this pointtrap - SIGINT# if the script gets here, we know only `ls` got killedprintf Got here! Only 'ls' got killed.\nexit 0However, if you want to retain the way you do things, with scannew being a function run as a background job, it takes a bit more work.Since you want user input, the proper way to do it is to use read, but we still need the script to go on if scannew completes and not just wait for user input forever. read makes this a bit tricky, because bash waits for the current command to complete before allowing traps to work on signals. The only solution to this that I know of, without refactoring the entire script, is to put read in a while true loop and give it a timeout of 1 second, using read -t 1. This way, it'll always take at least a second for the process to finish, but that may be acceptable in a circumstance like yours where you essentially want to run a polling daemon that lists usb devices.#! /usr/bin/env bashfunction 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 ittrap break SIGUSR1slow_background_work &while truedo    # 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                            }donetrap - SIGUSR1printf Welcome to the start of the rest of your script.\nexit 0Of course, if what you actually want is a daemon that watches for changes in the number of usb devices or something, you should look into systemd which might provide something more elegant.