spraff
spraff

Reputation: 33395

How can I make an external program interruptible in this trap-captured bash script?

I am writing a script which will run an external program (arecord) and do some cleanup if it's interrupted by either a POSIX signal or input on a named pipe. Here's the draft in full

#!/bin/bash

X=`date '+%Y-%m-%d_%H.%M.%S'`

F=/tmp/$X.wav
P=/tmp/$X.$$.fifo

mkfifo $P

trap "echo interrupted && (rm $P || echo 'couldnt delete $P') && echo 'removed fifo' && exit" INT

# this forked process will wait for input on the fifo
(echo 'waiting for fifo' && cat $P >/dev/null && echo 'fifo hit' && kill -s SIGINT $$)&

while true
do
    echo waiting...
    sleep 1
done
#arecord $F

This works perfectly as it is: the script ends when a signal arrives and a signal is generated if the fifo is written-to.

But instead of the while true loop I want the now-commented-out arecord command, but if I run that program instead of the loop the SIGINT doesn't get caught in the trap and arecord keeps running.

What should I do?

Upvotes: 1

Views: 250

Answers (1)

vastlysuperiorman
vastlysuperiorman

Reputation: 1784

It sounds like you really need this to work more like an init script. So, start arecord in the background and put the pid in a file. Then use the trap to kill the arecord process based on the pidfile.

#!/bin/bash
PIDFILE=/var/run/arecord-runner.pid #Just somewhere to store the pid
LOGFILE=/var/log/arecord-runner.log

#Just one option for how to format your trap call
#Note that this does not use &&, so one failed function will not
# prevent other items in the trap from running
trapFunc() {
    echo interrupted
    (rm $P || echo 'couldnt delete $P')
    echo 'removed fifo'
    kill $(cat $PIDFILE)
    exit 0
}

X=`date '+%Y-%m-%d_%H.%M.%S'`

F=/tmp/$X.wav
P=/tmp/$X.$$.fifo

mkfifo $P

trap "trapFunc" INT

# this forked process will wait for input on the fifo
(echo 'waiting for fifo' && cat $P >/dev/null && echo 'fifo hit' && kill -s SIGINT $$)&

arecord $F 1>$LOGFILE 2>&1 &  #Run in the background, sending logs to file
echo $! > $PIDFILE  #Save pid of the last background process to file
while true
do
    echo waiting...
    sleep 1
done

Also... you may have your trap written with '&&' clauses for a reason, but as an alternative, you can give a function name as I did above, or a sort of anonymous function like this:

trap "{ command1; command2 args; command3; exit 0; }"

Just make sure that each command is followed by a semicolon and there are spaces between the braces and the commands. The risk of using && in the trap is that your script will continue to run past the interrupt if one of the commands before the exit fails to execute (but maybe you want that?).

Upvotes: 1

Related Questions