9

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

Raffa
  • 34,963
Marc
  • 305

4 Answers4

12

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.

Raffa
  • 34,963
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.

FedKad
  • 13,420
5
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.
Rinzwind
  • 309,379
5

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.

steeldriver
  • 142,475