7

How can i check in a script if a certain process (PID) is still running?

e.g.
check if PID still running
if yes then keep checking
if not then echo PID has finished and send simple email

or 
Check if PID is not running
echo PID not running and send simple email
else
go back and check again

Additional Info: Mail will be a simple command that gets executed when the PID is gone, like mail -s "PID update" abc@gmail.com <<< "PID $pid has exited on $HOSTENAME on $(date)" –

Additional info: the PID will be active for several days and so the monitoring script has to continue checking in the background for a long time

4 Answers 4

9

Intoduction

From man kill(1):

If signal is 0, then no actual signal is sent, but error checking is still performed.

Therefore, to check if a process(that the current user/caller is permitted to signal) is running, you can use either /bin/kill or if your shell has a builtin kill command, then you can use that as well ... kill will exit with exit code 0(i.e. success) if the process exists and can be killed(by the current user/caller) and it will error, print an error message and exit > 0 otherwise.

Notice: If, the current user/caller is not permitted to signal the process, then please see the workarounds section below on how to do it.

Usage

In bash, however, you can use the shell builtin kill to do things like this:

pid="34223"

kill -0 "$pid" &> /dev/null && echo "$pid exists" || echo "$pid doesn't exist"

and this:

pid="34223"

if kill -0 "$pid" &> /dev/null;
  then
    echo "$pid exists"
  else
    echo "$pid doesn't exist"
  fi

and this:

pid="34223"

while :;
  do
  if ! kill -0 "$pid" &> /dev/null;
    then
     echo "$pid has exited"
     break
   else
     sleep 1
   fi
  done

or to run the above example from within a script that its PID is to be monitored without stopping the execution of the script, you can do this:

#!/bin/bash

# Put this at the top to be run in a background sub-shel i.e (...) & 

pid="$$"

(while :;
  do
  if ! kill -0 "$pid" &> /dev/null;
    then
     echo "$pid has exited"
     break
   else
     sleep 1
   fi
  done) &

# The rest of your code goes below this line

Without actually killing anything.

Workarounds

Notice: Process IDs in Ubuntu get recycled and reused for newly created processes once their old processes die and the same application/program most likely will get assigned different PIDs the next time it is run ... So, they are not unique and not persistent ... Therefore, if you want to know when a process has just started, you can use e.g. pgrep to find process by e.g command/name as PID doesn't make sense here:

cmd="my_command"

while :;
  do
  if pgrep -f "$cmd" &> /dev/null;
    then
     echo "$cmd has started"
     break
   else
     sleep 1
   fi
  done

Notice: as well that as stated in man kill(2):

If sig is 0, then no signal is sent, but existence and permission checks are still performed; this can be used to check for the existence of a process ID or process group ID that the caller is permitted to signal.

Therefore, if the the user running the above kill checks needs to check processes that belong to other users/groups, then they need to implement, instead of the above, something like this:

pid="34223"

if ! kill -0 "$pid" |& grep -q "No such process";
  then
    echo "$pid exists"
  else
    echo "$pid doesn't exist"
  fi

This will check the error message itself instead of the exit status for kill and therefore should work as the error message when the process ID exists but the signaling user isn't permitted to do so will read Operation not permitted while if that process doesn't exist, it will read No such process ... Therefore, checking for the latter should work.

7
  • The kill solution is elegant. The 2nd one is what I need but it blocks the shell when I call the script and PID is active (since the script remains in the loop). How can I have it check PID the background?
    – Marc
    Mar 18 at 15:22
  • 1
    1 big issue though with the kill command and this option: if you are not allowed to kill the process this will error out, The kill needs to be done with the same user (or the same group) as the running process. (this option works best from within the script itself).
    – Rinzwind
    Mar 18 at 15:31
  • @Marc You mean to run it from within the same script it monitors? ... I will update the answer in a moment for this.
    – Raffa
    Mar 18 at 15:32
  • @Raffa No - this script will monitor an independent PID (who's number I know). But when I launch your (monitoring) script, it goes into a loop and blocks the terminal I executed the script from. The PID may be there for several days so the loop will run for days, so I need the monitor to run in the background.
    – Marc
    Mar 18 at 15:38
  • 1
    @Marc put the whole loop in a sub-shell i.e. between ( and ) and send it to the background e.g. ( while .... done) &
    – Raffa
    Mar 18 at 15:42
5

Try the following for the first part:

#!/bin/bash
pid="$1"
while ps -p "$pid" >/dev/null ; do
  sleep 2
done
echo "PID $pid has finished"
mail …

And call it with a command like this:

$ nohup check_pid.sh 1234 &>/tmp/check_pid.log &

But, if you want something like the reverse of the above, try this:

#!/bin/bash
pid="$1"
while ! ps -p "$pid" >/dev/null ; do
  sleep 2
  ## echo "PID $pid is not running."
done
echo "PID $pid has started."

Practically, this second example is not meaningful, because you will never know the PID (that will assigned by the kernel) to a newly started process.

2
  • 1
    Like it too so +1 :) I used the proc version as ps tends to be different across implementations. The 3rd one would be kill 0 $PID
    – Rinzwind
    Mar 18 at 13:48
  • Well 1st and 2nd are the same problem (hence the word or). And I do know the PID. Its a process that will run for several days and once its in ps, I can start my script that will continuously check for the PID and send me an email once the PID is non existent anymore.
    – Marc
    Mar 18 at 15:13
4
if test -d /proc/"$PID"/; then
    echo "process exists"
fi

can be used to see if a PID is present. But that does not have to mean it is active. Generally I would do that by having the process create a file and write to it at specific places (like in a while loop). As long as that happens the process is doing something.

  • $PID if empty will return true (as /proc/ exists on systems using procfs) so be careful of that.
  • this method is portable as all Linux use procfs. Mac does too iirc.
4

For processes started from the same shell, you can use the bash builtin wait command ex.

# start a "long running" process in the background
echo "starting process" | ts
sleep 30 &

# get the last backgrounded job's PID and wait for it to finish
wait $! && echo "Process $! finished" | ts

-->

Mar 18 10:05:51 starting process
[1] 18127
[1]+  Done                    sleep 30
Mar 18 10:06:21 Process 18127 finished

This will likely have less overhead than explicitly looping and checking the kernel's process list.

1
  • 1
    @Marc I agree with steeldriver. Please add extra info into the question. Some of us might want to change the answer ;)
    – Rinzwind
    Mar 18 at 15:35

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.