James M. Lay James M. Lay - 20 days ago 5
Linux Question

Allow user to ^C child process instead of whole script

I'm writing a script that will fetch a list of database names from a server so the user knows which databases they can enter in the next prompt:

echo "FETCHING DB NAMES... (^C to skip)"
ssh "$user@$SERV_A" "$remote_cmd" #returns space-delim db list
echo
printf "Which db do you want to import? >"
read db_name
#rest of script


It looks like this to the user:

FETCHING DB NAMES... (^C to skip)
db1 db2 personnel

Which db would you like to import? >


The problem is that fetching those databases happens can take between 1-10 seconds depending on the connection. If the user already knows the db name, this can be frustrating.

Of course, pressing
^C
as the prompt indicates kills the entire script rather than just the ssh process. Is there a way I can write the script so they can cancel/skip just that ssh process?

Thank you in advance.

EDIT: it doesn't have to be
^C
per se, really just want to give the user a way to skip a running child process.

Answer

Here is a possible way using trap on SIGQUIT (Ctrl+\)

(For this example I am substituting your ssh command for a simple sleep 5 ; echo "names" to simulate an output from ssh)

#!/bin/bash
bashpid="$$"
trap "kill -9 "$bashpid"" SIGINT

echo "FETCHING DB NAMES... (^\ to skip)"         # Fetching text
dbnames="$(sleep 5 ; echo "names")"              # ssh command - output saved in $dbnames
trap "kill -9 "$!"" SIGQUIT
echo "$dbnames"
if [[ -n "$dbnames" ]]; then                     # Only ask import if dbnames variable
    printf "Which db do you want to import? >"   # isn't empty, otherwise
    read db_name                                 # skip this whole
fi                                               # section 
echo "Rest of script"

There are quite a few other ways, including looping read statements for input, and using SIGCHLD and/or other internals that may or may not require certain shell options present. For the sake of skipping one task, this might be reliable.

This also traps ctrl+c to ensure that it kills the entire script (Sometimes scripts produce what you're after as an unwanted effect, and sometimes playing around with subshell methods and traps can cause it), so this is just to ensure that:

  • Ctrl+C kills everything
  • Ctrl+\ kills the subtask

Running the script above and pressing Ctrl+C:

  • FETCHING DB NAMES... (^\ to skip)
    ^CKilled: 9

Running the script above and pressing Ctrl+\:

  • FETCHING DB NAMES... (^\ to skip)
    ^\
    Rest of script

Running the script above and leaving it to run (a successful ssh result)

  • FETCHING DB NAMES... (^\ to skip)
    names
    Which db do you want to import? >

Obviously there would be some more refining to tune it to your exact environment, but the boilerplate is there.