Reputation: 16785
I start a background process from my shell script, and I would like to kill this process when my script finishes.
How to get the PID of this process from my shell script? As far as I can see variable $!
contains the PID of the current script, not the background process.
Upvotes: 554
Views: 416342
Reputation: 1440
Some years late, here's a one-liner to find the PID of a command :
read -p "What is the name if the command to find PID of ? " pname ; jobs -l | grep $pname | awk '{ print "The command PID is "$2}
What it does : Ask for the process name variable pname list all jobs and search for $pname with grep use awk to return the result legibly.
Easily amended to stop a process for example, by modifying the awk step and passing to bash
read -p "What is the name if the command to find PID of ? " pname ; jobs -l | grep $pname | awk '{ print "kill "$2} | bash
or non interactively for any process/command "foo" it's very succinct :
set pname=foo ; jobs -l | grep $pname | awk '{ print "The command PID is "$2}
though note this assumes there is just one process named foo. In the unlikely event one were in that situation, one could add a test for number of answers, and if number greater than one, propose a list of process IDs
Upvotes: 0
Reputation: 51
If you're not able to get the PID directly after starting the job, you could also try this and get the PID later:
foo &
# do some stuff and then
pid=$(ps -aux | grep foo | grep -v grep | tr -s ' ' | cut -d\ -f2)
kill $pid
Explanation:
ps
get information for all processes incl. commandgrep
is filtering for your command and the second grep is for not retrieving the pid of grep itselftr
is removing duplicate spaces for cutcut
is getting you the column with the PID (2 in this case)Upvotes: 1
Reputation: 125
I have run into this problem many times provisioning various infrastructure objects. Many times you need a temp proxy using kubectl or a temp port forward. I have found the timeout command to be a good solution for these, since it allows my script to be self contained and I can be assured that the process will end. I try to set small timeouts and rerun the script if I need still need it.
Upvotes: 0
Reputation: 96716
You can use the jobs -l
command to get to a particular jobL
^Z
[1]+ Stopped guard
my_mac:workspace r$ jobs -l
[1]+ 46841 Suspended: 18 guard
In this case, 46841 is the PID.
From help jobs
:
-l Report the process group ID and working directory of the jobs.
jobs -p
is another option which shows just the PIDs.
Upvotes: 203
Reputation: 1239
You might also be able to use pstree:
pstree -p user
This typically gives a text representation of all the processes for the "user" and the -p option gives the process-id. It does not depend, as far as I understand, on having the processes be owned by the current shell. It also shows forks.
Upvotes: 4
Reputation: 1201
An even simpler way to kill all child process of a bash script:
pkill -P $$
The -P
flag works the same way with pkill
and pgrep
- it gets child processes, only with pkill
the child processes get killed and with pgrep
child PIDs are printed to stdout.
Upvotes: 38
Reputation: 6476
$$
is the current script's pid$!
is the pid of the last background processHere's a sample transcript from a bash session (%1
refers to the ordinal number of background process as seen from jobs
):
$ echo $$
3748
$ sleep 100 &
[1] 192
$ echo $!
192
$ kill %1
[1]+ Terminated sleep 100
Upvotes: 74
Reputation: 349
pgrep
can get you all of the child PIDs of a parent process. As mentioned earlier $$
is the current scripts PID. So, if you want a script that cleans up after itself, this should do the trick:
trap 'kill $( pgrep -P $$ | tr "\n" " " )' SIGINT SIGTERM EXIT
Upvotes: 4
Reputation: 69
this is what I have done. Check it out, hope it can help.
#!/bin/bash
#
# So something to show.
echo "UNO" > UNO.txt
echo "DOS" > DOS.txt
#
# Initialize Pid List
dPidLst=""
#
# Generate background processes
tail -f UNO.txt&
dPidLst="$dPidLst $!"
tail -f DOS.txt&
dPidLst="$dPidLst $!"
#
# Report process IDs
echo PID=$$
echo dPidLst=$dPidLst
#
# Show process on current shell
ps -f
#
# Start killing background processes from list
for dPid in $dPidLst
do
echo killing $dPid. Process is still there.
ps | grep $dPid
kill $dPid
ps | grep $dPid
echo Just ran "'"ps"'" command, $dPid must not show again.
done
Then just run it as: ./bgkill.sh
with proper permissions of course
root@umsstd22 [P]:~# ./bgkill.sh
PID=23757
dPidLst= 23758 23759
UNO
DOS
UID PID PPID C STIME TTY TIME CMD
root 3937 3935 0 11:07 pts/5 00:00:00 -bash
root 23757 3937 0 11:55 pts/5 00:00:00 /bin/bash ./bgkill.sh
root 23758 23757 0 11:55 pts/5 00:00:00 tail -f UNO.txt
root 23759 23757 0 11:55 pts/5 00:00:00 tail -f DOS.txt
root 23760 23757 0 11:55 pts/5 00:00:00 ps -f
killing 23758. Process is still there.
23758 pts/5 00:00:00 tail
./bgkill.sh: line 24: 23758 Terminated tail -f UNO.txt
Just ran 'ps' command, 23758 must not show again.
killing 23759. Process is still there.
23759 pts/5 00:00:00 tail
./bgkill.sh: line 24: 23759 Terminated tail -f DOS.txt
Just ran 'ps' command, 23759 must not show again.
root@umsstd22 [P]:~# ps -f
UID PID PPID C STIME TTY TIME CMD
root 3937 3935 0 11:07 pts/5 00:00:00 -bash
root 24200 3937 0 11:56 pts/5 00:00:00 ps -f
Upvotes: 6
Reputation: 42418
You need to save the PID of the background process at the time you start it:
foo &
FOO_PID=$!
# do other stuff
kill $FOO_PID
You cannot use job control, since that is an interactive feature and tied to a controlling terminal. A script will not necessarily have a terminal attached at all so job control will not necessarily be available.
Upvotes: 826