I have a number of processes launched via a bash script, listening for and playing, via netcat, audio streams arriving on separate ports:

# listener.sh

while :
    nc -l 900$1 | aplay - 
    sleep 1

exit 0

This script is launched in another script with arguments to define unique ports. eg.

    # startlisterners.sh

    if [ ! -f /tmp/listener1.pid ]; then
         nohup  listener.sh 1 &
        echo $! > /tmp/listener1.pid
    if [ ! -f /tmp/listener2.pid ]; then
         nohup  listener.sh 2 &
         echo $! > /tmp/listener2.pid

    ..... etc.

    exit 0

I need, periodically, to be able to selectively kill off instances of "aplay" in the subscripts while keeping the subscripts themselves running.

How can I access the individual PIDs of each aplay process?


Instead of

nc -l 900$1 | aplay - 
sleep 1


nc -l "900$1" | aplay - &
   # here you can use $!
sleep 1

Now where it reads # here you can use $! you can e.g. write $! to a predefined file. This approach may require listener.sh to know its number (listener1, listener2 etc.) so it can use a file with the right number.

Another way is to define a trap before while in listener.sh:

trap 'kill "$!"' SIGUSR2

With this implemented, if you send SIGUSR2 (kill -s SIGUSR2 …) to a listener.sh process then it will kill its own aplay. You already know the PIDs of your listeners thanks to /tmp/listener*.pid files.

Mind what the manual says:

If Bash is waiting for a command to complete and receives a signal for which a trap has been set, the trap will not be executed until the command completes. When Bash is waiting for an asynchronous command via the wait builtin, the reception of a signal for which a trap has been set will cause the wait builtin to return immediately with an exit status greater than 128, immediately after which the trap is executed.

This means you should run aplay in the background and wait for it (like above).

I can only guess you run multiple listeners to be able to serve multiple clients (still at most one client per port at any given time). Then you occasionally need to kill aplay, e.g. when a client silently disappears and nc is not notified.

Maybe this entire approach can be replaced by a single socat:

socat TCP-LISTEN:9001,fork EXEC:'aplay -'

Thanks to fork it can serve multiple clients on the same port. Use the -T option (see man 1 socat) to terminate stale connections. Even without -T they should timeout eventually anyway. The solution is vulnerable to a DOS attack though.

  • Many thanks. I went with your "trap 'kill "$!"' SIGUSR2" suggestion and multiple listeners, although netcat continued to give problems so I changed the netcat line in listener.sh to socat TCP-LISTEN:900$1 EXEC:'aplay -'. This seems very solid. Your last example, unless I'm mistaken, I can't use as I would need to be able to kill the individual forked aplay process and I can't see a way of doing that. Out of curiosity, in the listener.sh script, is there a way of getting the pid of nc (now socat in my script) in addition to aplay? All the best, – sebinho Apr 10 '20 at 12:18
  • @sebinho This question and this, and this one. – Kamil Maciorowski Apr 10 '20 at 12:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.