2
#!/bin/bash

mkfifo /var/run/out
while true
do
    cat /var/run/out | nc -l 8080 > >(
        while read line
        do
            do some stuff
        done
    )
done

How can I get the pid of nc and cat? Using $! doesn't work. I want to write them to a pid file.

3 Answers3

4

There may be a more elegant way of doing what you want, but this works:

do
    (cat /var/run/out& echo $! > cat_pid; wait) |
                            (nc -l 8080 <&0 & echo $! > nc_pid; wait) > >(
                ︙

(broken into separate lines after the | for readability).  This works by wrapping each command whose PID you want in a subshell, running it asynchronously in that subshell, capturing its PID, and then waiting for the command to finish.  (This, in effect, puts the command back into the foreground.)  Use Scott’s <&0 trick to get an asynchronous process in a script to read its rightful standard input.

But why are you using “cat”? And why are you using > >(…) instead of simple piping?

You could simplify this a little as:

while true
do
    (nc -l 8080 < /var/run/out & echo $! > nc_pid; wait) |
                while read line
                do
                    do some stuff
                done
done

letting nc read directly from /var/run/out, and using a simple pipe instead of process substitution.

  • The code was taken from another answer on stack overflow. Your idea with pipes sounds better, can you show me how you would write it out? – Cameron Ball Aug 19 '14 at 01:59
  • This seems like a much better way. Thank you. – Cameron Ball Aug 19 '14 at 03:42
  • Sorry, I have to unaccept because the script no longer behaves in the same way. If I start it up using your code, then access the machine through a browser on port 8080, it never gets to the 'do some stuff' part. – Cameron Ball Aug 19 '14 at 03:48
  • Are you talking about my first answer or my revised answer? Does the "do some stuff" write to /var/run/out? It occurred to my last night that might cause deadlock that might be an issue, and might be why the original author(s) chose the cat … |. Try going back to my first answer; if that works, then maybe try changing the > >(…) to | …. – G-Man Says 'Reinstate Monica' Aug 19 '14 at 15:35
  • The revised answer results in a deadlock, yes. I'll test out the first part some more and accept if I can get it to work. – Cameron Ball Aug 20 '14 at 15:44
1

Simplest way I can think of is to get the pid before you launch the process.

For example (in bash):

pidfile() { (
    echo $BASHPID > "$1"
    shift
    exec "$@"
) }

mkfifo /var/run/out
while true
do
    pidfile /tmp/cat.pid cat /var/run/out | pidfile /tmp/nc.pid nc -l 8080 > >(
        while read line
        do
            do some stuff
        done
    )
done

This creates a pidfile function that writes the current PID to the specified file, and then executes the rest of the arguments using exec. The exec causes the command to be run with the same pid as the shell that just wrote out the file.

We wrap the entire pidfile() function inside parenthesis () so that we can ensure it executes in a subshell. The situation where this becomes critical is when you execute a command that isn't in a pipeline. For example:

pidfile /tmp/foo some command here

When you do it this way, pidfile() is going to run in the same process as the rest of the script, and so when it calls exec, the command being run will take over the PID, and the script will no longer be running.

Also note, that the above is all written for bash. $BASHPID is a bash specific variable. $$ does not change when using a subshell, so the value is incorrect for our use.

phemmer
  • 71,831
0

You can't get the PID of cat or nc in that script unless you background the processes with &. Any process you don't background is going to have to finish before the script continues.

The Bash wiki process management article explains how to do such things sanely.

l0b0
  • 51,350