10

If, in bash, I execute:

cmd1 | cmd2 | ... | cmdi | ... | cmdn &

where the cmd{1..n} may not be distinct, how do I get the PID of cmdi? Alternatively, how can I signal the cmdi process? (For example, send it SIGUSR1?) pkill/pgrep, pidof etc. don't look like good answers, since other instances of cmdi maybe running, including as part of the same pipeline. jobs -p gives the PID of cmd1, for me.

i can be anything in {1..n}.

  • possible duplicate of How can I get the pid of a process started this way – G-Man Sep 18 '14 at 19:34
  • 1
    @G-Man Care to explain? I see only superficial similarity, and as I explained in Ramesh's answer, modifying the set of commands is of not much use. – muru Sep 18 '14 at 19:37
  • Superficial similarity? cat /var/run/out | nc -l 8080 is only superficially similar to cmd1 | cmd2? Your constraint, that you want to type the bare-bones pipeline and then recover the PIDs, is (1) not stated in the question, and (2) unlikely to allow for a good, general solution. – G-Man Sep 18 '14 at 19:43
  • @G-Man On the contrary, you are imposing constraints that simple aren't stated. cmd1 | cmd2 is a very special case where both PIDs are easily obtainable. Did I say anything about n? So why would you assume n=2? Did I say anything about what cmdi is? So why would you assume I could modify cmdi? I am asking for a general solution and you are imposing restrictions. – muru Sep 18 '14 at 19:48
6

For the original version of the question, when only the last command's PID was desired, the special variable $! is perfect.

foo | bar | baz &
baz_pid=$!

There's no similar easy access to the PIDs of the other processes.

It took a long time for $pipestatus (zsh) and $PIPESTATUS (bash) to be added, finally giving us access to all of the exit statuses in a pipeline, in addition to the $? for the last one that has been around since the original Bourne shell. Maybe something analogous will happen with $! eventually.

  • Would you mind if I edited the question to ask for the PID of an arbitrary command in the list as well? Or should I start a new question? – muru Sep 18 '14 at 0:21
  • You'll probably have to wait a lot longer for an answer to that one. I don't have strong feelings about stackexchange site organization so separate question, edit question, whatever... won't bother me – Wumpus Q. Wumbley Sep 18 '14 at 0:23
  • That's okay, the immediate problem is solved, now curiosity is in charge. I'll edit it then. Just a heads-up, since I have seen questions change drastically and leaving the older answers looking very out-of-place. – muru Sep 18 '14 at 0:26
  • @muru - note it would've been better to make it a new Q referencing this one. – slm Sep 18 '14 at 2:12
  • @slm duly noted. Will do so in future. – muru Sep 18 '14 at 2:15
4

I think you could do something as suggested here.

(ls -l | echo "Hello" | df -h & echo $! >&3 ) 3>pid

Here in the above example, I have retrieved the pid of third piped process and noted it down to the file pid. I could note it down for any piped process.

  • Interesting, but this would involve modifying the set of commands. Not much use once the commands have been executed. – muru Sep 18 '14 at 0:49
  • @muru - what? what use is any PID once it has finished execution? do you want the pipeline's PID? jobs -p. signal it with SIGPIPE. Do you want cmdi - this. – mikeserv Sep 18 '14 at 4:35
  • 1
    @mikeserv Not if they are in the background, running as we speak. By what sorcery am I supposed to modify the command line for that? – muru Sep 18 '14 at 5:41
  • 1
    @muru that would be a sorcery. you need a debugger. – mikeserv Sep 18 '14 at 5:43
  • I find this to be a useful pattern for starting background processes, waiting for them to reach some state, and then killing them. In case anybody is interested: gist.github.com/MatrixManAtYrService/… – MatrixManAtYrService May 6 at 2:41
2

A not-very-portable, Linux-specific solution could be to track the processes using the pipes that connect them. We can get the PIDs of the first (jobs -p) and last ($!) commands in the pipeline. Using either PID, this script could do the job:

#! /bin/bash

PROC=$1
echo $PROC

if [[ $(readlink /proc/$PROC/fd/1) =~ ^pipe: ]]
then
    # Assuming first process in chain...
    NEXT_FD=1
elif [[ $(readlink /proc/$PROC/fd/0) =~ ^pipe: ]]
then
    # Last process in chain...
    NEXT_FD=0
else
    # Doesn't look like a pipe.
    exit
fi

NEXT_PROC_PIPE=$(readlink /proc/$PROC/fd/$NEXT_FD)

while [[ $NEXT_PROC_PIPE =~ ^pipe: ]] 
do
    PROC=$(find /proc/*/fd -type l -printf "%p/%l\n" 2>/dev/null | awk -F'/' '($6 == "'"$NEXT_PROC_PIPE"'") && ($3 != "'$PROC'" ) {print $3}')
    NEXT_PROC_PIPE=$(readlink /proc/$PROC/fd/$NEXT_FD)
    echo $PROC
done

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.