There are three steps involved in solving your problem:
Execute a command in the background, so it will keep running while your script does something else. You can do this by following the command with &
. See the section on Job Control in the Bash Reference Manual for more details.
Keep track of that command's status, so you'll know if it is still running. You can do this with the special variable $!
, which is set to the PID (process identifier) of the last command you ran in the background, or empty if no background command was started. Linux creates a directory /proc/$PID
for every process that is running and deletes it when the process exits, so you can check for the existence of that directory to find out if the background command is still running. You can learn more than you ever wanted to know about /proc
from the Linux Documentation Project's File System Hierarchy page or Advanced Bash-Scripting Guide.
Kill the background command if your script is killed. You can do this with the trap
command, which is a bash builtin command.
Putting the pieces together:
# Look for the 4 common signals that indicate this script was killed.
# If the background command was started, kill it, too.
trap '[ -z $! ] || kill $!' SIGHUP SIGINT SIGQUIT SIGTERM
cp $SOURCE $DEST & # Copy the file in the background.
# The /proc directory exists while the command runs.
while [ -e /proc/$! ]; do
echo -n "." # Do something while the background command runs.
sleep 1 # Optional: slow the loop so we don't use up all the dots.
done
Note that we check the /proc
directory to find out if the background command is still running, because kill -0
will generate an error if it's called when the process no longer exists.
Update to explain the use of trap
:
The syntax is trap [arg] [sigspec …]
, where sigspec …
is a list of signals to catch, and arg
is a command to execute when any of those signals is raised. In this case, the command is a list:
'[ -z $! ] || kill $!'
This is a common bash idiom that takes advantage of the way ||
is processed. An expression of the form cmd1 || cmd2
will evaluate as successful if either cmd1
OR cmd2
succeeds. But bash is clever: if cmd1
succeeds, bash knows that the complete expression must also succeed, so it doesn't bother to evaluate cmd2
. On the other hand, if cmd1
fails, the result of cmd2
determines the overall result of the expression. So an important feature of ||
is that it will execute cmd2
only if cmd1
fails. That means it's a shortcut for the (invalid) sequence:
if cmd1; then
# do nothing
else
cmd2
fi
With that in mind, we can see that
trap '[ -z $! ] || kill $!' SIGHUP SIGINT SIGQUIT SIGTERM
will test whether $!
is empty (which means the background task was never executed). If that fails, which means the task was executed, it kills the task.