How to get the PID of a process that is piped to another process in Bash?
Solution 1
Write tail's PID to file descriptor 3, and then capture it from there.
( tail -f $1 & echo $! >&3 ) 3>pid | nc -l -p 9977
kill $(<pid)
Solution 2
Another option: use a redirect to subshell. This changes the order in which background processes are started, so $! gives PID of the tail
process.
tail -f $1 > >(nc -l -p 9977) &
wait $!
Solution 3
how about this:
jobs -x echo %1
%1
is for first job in chain, %2
for second, etc. jobs -x
replaces job specifier with PID.
Solution 4
This works for me (SLES Linux):
tail -F xxxx | tee -a yyyy &
export TAIL_PID=`jobs -p`
# export TEE_PID="$!"
The ps|grep|kill
trick mentioned in this thread would not work if a user can run the script for two "instances" on the same machine.
jobs -x echo %1
did not work for me (man page not having the -x
flag) but gave me the idea to try jobs -p
.
Solution 5
Maybe you could use a fifo, so that you can capture the pid of the first process, e.g.:
FIFO=my_fifo
rm -f $FIFO
mkfifo $FIFO
tail -f $1 > $FIFO &
TAIL_PID=$!
cat $FIFO | nc -l -p 9977
kill $TAIL_PID
rm -f $FIFO
Ertuğ Karamatlı
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Updated on May 14, 2020Comments
-
Ertuğ Karamatlı over 3 years
I am trying to implement a simple log server in Bash. It should take a file as a parameter and serve it on a port with netcat.
( tail -f $1 & ) | nc -l -p 9977
But the problem is that when the netcat terminates, tail is left behind running. (Clarification: If I don't fork the tail process it will continue to run forever even the netcat terminates.)
If I somehow know the PID of the tail then I could kill it afterwards.
Obviously, using $! will return the PID of netcat.How can I get the PID of the tail process?
-
Ertuğ Karamatlı about 14 yearsYes I have tried It before. The problem about using fifo is the same: pipe never gets terminated so cat stays running even netcat terminates. Also the control stays in the cat line so it never executes kill.
-
martin clayton about 14 yearsThat's odd - the script above worked perfectly for me on Mac OS X. Only slight difference was that I omitted the '-p' flag for nc.
-
Ertuğ Karamatlı about 14 yearsMaybe its a platform issue (about how to handle pipes). I'm trying it on a linux machine. thanks for your answer anyway!
-
Ertuğ Karamatlı about 14 yearsOh, I have never thought of it. I wish it worked :) But it's still not terminating because of the nature of "tail -f"
-
Ertuğ Karamatlı about 14 yearswithout the -f, its OK. But I want to serve it in real time.
-
Ertuğ Karamatlı about 14 yearsI couldn't get it using the ppid because it is detached when I fork it in a subshell. But I managed to grep it using the args param of the
ps
program. -
tripleee about 12 yearsShouldn't the PID of the tail command be available in
$!
so that you could simply dokill $!
instead ofkill_tail
? -
Wernight almost 12 yearsI used a variant:
( tail -f $1 & echo $! >pid ) | nc -l -p 9977
(not sure why using file descriptor 3 would help when finally redirecting to a file) -
Wernight almost 12 yearsNot sure why but my solution fails after a couple of log lines are output. Probably when the pipe buffer is full. Then the initial process seems to be waiting for the pipe to be processed.
-
hfs over 11 yearsWhat if you remove the
cat
and usenc -l -p 9977 < $FIFO
? -
Bryan Larsen almost 10 yearsThe advantage of this approach is that after the wait, $? also holds the exit status
-
William Luc Ritchie about 9 yearsThis seems like the cleanest approach by a long shot. Does it change anything (compared to standard piping) from the perspective of either process other than the start order?
-
William Luc Ritchie about 9 yearsActually, the syntax on that redirection is wrong (though the principle is correct).
>(foo)
is substituted for the name of the new file descriptor, whereas> >(foo)
actually redirects output to it. You want the first line to betail -f $1 > >(nc -l -p 9977) &
. -
JATothrim almost 9 yearsThis must be the cleanest + shortest solution to 'how to get pid of any process earlier in chain' Thank You! It also works with '&' getting the pid of process earlier in the chain that is running in background! E.g.
dd if=/dev/urandom bs=1M count=1024 | sha1sum & pid=$(jobs - x echo %1)
kill -USR1 $pid
-
pipe_of_sharks almost 5 yearsClosing stdin of
tail
with eg.</dev/null
orexec
as in the example is nice to do, so that no streams are left dangling around unconnected, but not strictly necessary for thetail
command in particular, sincetail
never reads from stdin when given a filename. Could be left out when the firstcoproc
command istail
. -
sl0815 over 4 yearsexactly what I needed! Thanks! Just redirecting the pid didn't work in my case.
-
TimeS over 4 yearsIf you want to capture both stdout and stderr in the subshell use
&>
for the redirection. Example:tail -f $1 &> >(nc -l -p 9977) &
. -
ivan_pozdeev over 3 yearsThe answer is wrong. Only one job ID is assigned, for the entire pipeline:
shopt -s lastpipe; sleep 1 | cat | jobs
->[1] Running sleep 1 | cat
. It has the PID of the 1st command on the pipeline butjobs -l
prints all the PIDs:shopt -s lastpipe; sleep 1 | cat | jobs -l; jobs -p
->[1] 5394 Running sleep 1
5395 | cat
5394
. -
ivan_pozdeev over 3 yearsNote that you can only get the PID of the first command on the pipeline this way.
jobs -l
would print PIDs of all commands but you'll have to parse its output to extract them. -
ivan_pozdeev over 3 yearsNote that
coproc
is new in Bash 4 (e.g. OSX still uses 3.2). It is available in OSX'szsh
though. -
Fabian Streitel about 3 yearsUnlike many of the other comments, this also works in
/bin/sh
to obtain the PID of the first command (process group leader), e.g. to be able to kill the entire pipeline later on. -
Apiwat Chantawibul almost 2 yearsCorrection: pipe
|
also doesn't wait for tail to finish. The differences are a bit more interesting.