245

In a bash script, I want to do the following (in pseudo-code):

if [ a process exists with $PID ]; then

    kill $PID 

fi

What's the appropriate expression for the conditional statement?

codeforester
  • 39,467
  • 16
  • 112
  • 140
Richard H
  • 38,037
  • 37
  • 111
  • 138

11 Answers11

343

The best way is:

if ps -p $PID > /dev/null
then
   echo "$PID is running"
   # Do something knowing the pid exists, i.e. the process with $PID is running
fi

The problem with kill -0 $PID is that the exit code will be non-zero even if the process is running and you don't have permission to kill it. For example:

kill -0 $known_running_pid

and

kill -0 $non_running_pid

have a non-zero exit codes that are indistinguishable for a normal user, but one of them is by assumption running, while the other is not.


Partly related, additional info provided by AnrDaemon: The init process (PID 1) is certainly running on all Linux machines, but not all POSIX systems are Linux. PID 1 is not guaranteed to exist there:

kill -0 1 
-bash: kill: (1) - No such process … 

DISCUSSION

The answers discussing kill and race conditions are exactly right if the body of the test is a "kill". I came looking for the general "how do you test for a PID existence in bash".

The /proc method is interesting, but in some sense breaks the spirit of the ps command abstraction, i.e. you don't need to go looking in /proc because what if Linus decides to call the exe file something else?

Shane Bishop
  • 3,905
  • 4
  • 17
  • 47
FDS
  • 4,999
  • 2
  • 22
  • 13
  • 1
    ps -p always returns status of 0 for me – IttayD Feb 23 '14 at 14:51
  • 1
    ps -p #### worked fine for me under Ubuntu 14.04, +1 thanks! – akahunahi Feb 23 '16 at 17:43
  • 3
    ps -p always returns status code 0 in os x because it prints an empty list of process when it not matches any running process – Douglas Correa Feb 24 '16 at 01:46
  • I can confirm that on macOS Sierra, this works. Also, the `-p` is unnecessary, at least in that case. `ps $PID` has the exact same result. – user137369 Apr 16 '17 at 10:56
  • Portability is a good reason to avoid using /proc, but linux breaking its ABI is not a scenario I would particularly worry about. – David Roundy Sep 08 '17 at 23:18
  • 4
    This does not work on BusyBox. OpenWrt only recognizes `w` for "wide output", Alpine takes only `-o` for filtering output columns. [Docs](https://busybox.net/downloads/BusyBox.html) also mention a `-T` for showing threads. There is no `-p` parameter. – antichris Aug 22 '19 at 21:58
  • 2
    If you want to know if the process with id $PID exists, you can just do `test -d /proc/$PID` instead of starting additional processes. Note that you cannot ever know if a process exists in some another PID namespace. – Mikko Rantalainen Sep 22 '20 at 12:42
  • Actually, test is often shell builtin and only executes syscalls by the shell itself. – Mikko Rantalainen Sep 25 '20 at 05:06
  • Just for other noobs like I seem to be: `> /dev/null` does not read `> 0`(comparison), but forwards the output to nothingness. I just proposed an update to your brilliant and +1ed answer and incorporated the comment by [AnrDaemon](https://stackoverflow.com/users/1449366/anrdaemon). Alternatively to `ps -p $PID > /dev/null`, you can internally check (without a new process) whether a `PID` is running with `[[ ! -z ${PID+x} ]]`, as [statet e.g. here](https://stackoverflow.com/a/52415553/16958560). – Cadoiz Oct 14 '21 at 13:58
  • `ps -p` seems to be the correct solution for RHEL – Richard Tyler Miles Jan 13 '22 at 03:10
213

To check for the existence of a process, use

kill -0 $pid

But just as @unwind said, if you want it to terminate in any case, then just

kill $pid

Otherwise you will have a race condition, where the process might have disappeared after the first kill -0.

If you want to ignore the text output of kill and do something based on the exit code, you can

if ! kill $pid > /dev/null 2>&1; then
    echo "Could not send SIGTERM to process $pid" >&2
fi
Christoffer Hammarström
  • 27,242
  • 4
  • 49
  • 58
  • 1
    looking at the man page, kill -0 is: "exit code indicates if a signal may be sent". So does this actualy kill a process, or just tell you if it can be killed? – Richard H Jun 15 '10 at 09:53
  • 38
    `kill` is somewhat misnamed in that it doesn't necessarily kill the process. It just sends the process a signal. `kill $PID` is equivalent to `kill -15 $PID`, which sends signal 15, SIGTERM to the process, which is an instruction to terminate. There isn't a signal 0, that's a special value telling `kill` to just check if a signal could be sent to the process, which is for most purposes more or less equivalent to checking if it exists. See http://linux.die.net/man/2/kill and http://linux.die.net/man/7/signal – Christoffer Hammarström Jun 15 '10 at 10:58
  • 54
    This has the problem that if the process is not owned by the running user, you may not have permissions to call kill -0. Better to use ps -p $PID > /dev/null 2>&1, which allows you to see process status, even if you do not have permissions to send a signal. – mckoss Apr 01 '12 at 16:55
  • 9
    @mckoss: In that case he can't kill it anyway. – Christoffer Hammarström Apr 01 '12 at 17:13
  • 4
    So, I guess - to use `kill -0`, I, in fact, have to do this: `kill -0 25667 ; echo $?` - and then if I get a `0` returned, then the process with that PID can be killed; and if the process PID (say) doesn't exist, the `$?` will be `1`, indicating a failure. Is that correct? – sdaau May 01 '13 at 05:04
  • 5
    @sdaau: Read again. If you're going to kill it anyway, then just kill it, otherwise you will have a race condition. But yes, an exit code of 0 means that it was possible to send a signal to it at that time. It does not mean that you can be sure that you can send a signal to it a millisecond later. – Christoffer Hammarström May 01 '13 at 10:21
  • 2
    If for some reason $PID is 0 (e.g. it was supposed to be read from a pid file, but file is missing and some logic decides to place a "0" instead), then kill -0 0 returns error code 0, as if process pid exists! Beware of this pitfall. – Mihnea Simian Dec 17 '13 at 09:42
  • If $PID is 0, then the signal is sent to every process in the process group of the calling process. – Christoffer Hammarström May 25 '21 at 10:07
88

On systems that implement procfs interface such as Linux, you can just check if /proc/$PID exists:

if test -d /proc/"$PID"/; then
    echo "process exists"
fi

otherwise you can use ps program:

if [ -n "$(ps -p $PID -o pid=)" ]

In the latter form, -o pid= is an output format to display only the process ID column with no header. The quotes are necessary for non-empty string operator -n to give valid result.

VasiliNovikov
  • 9,681
  • 4
  • 44
  • 62
user2683246
  • 3,399
  • 29
  • 31
  • 2
    The second method works on Mac too, as an added plus (Mac OS X has no /proc FS). You can, however, avoid using a subshell and use this on both Mac and Linux: `if ps -p"$PID" -o "pid=" >/dev/null 2>&1; then echo "Process is running..."; fi` – Will Mar 25 '15 at 07:31
  • Unfortunately, `ps` options and features tend to vary between platforms, so it's still not entirely portable. – tripleee Jul 27 '16 at 09:45
  • 3
    if `$PID` is empty then `[ -e /proc/$PID ]` will still return true, since the `/proc/` directory still exists. – Magne Feb 26 '18 at 13:20
  • 1
    `-e /proc/$PID/status` makes `-n "$PID"` check unnecessary, because there is no `/proc//status` file. – Maxim Egorushkin Nov 16 '21 at 16:45
41

ps command with -p $PID can do this:

$ ps -p 3531
  PID TTY          TIME CMD
 3531 ?        00:03:07 emacs
devnull
  • 118,548
  • 33
  • 236
  • 227
oherrala
  • 701
  • 4
  • 10
13

You have two ways:

Lets start by looking for a specific application in my laptop:

[root@pinky:~]# ps fax | grep mozilla
 3358 ?        S      0:00  \_ /bin/sh /usr/lib/firefox-3.5/run-mozilla.sh /usr/lib/firefox-3.5/firefox
16198 pts/2    S+     0:00  \_ grep mozilla

All examples now will look for PID 3358.

First way: Run ps aux and grep for the PID in the second column. In this example I look for firefox, and then for it's PID:

[root@pinky:~]# ps aux | awk '{print $2 }' | grep 3358
3358

So your code will be:

if [ ps aux | awk '{print $2 }' | grep -q $PID 2> /dev/null ]; then
    kill $PID 
fi

Second way: Just look for something in the /proc/$PID directory. I am using exe in this example, but you can use anything else.

[root@pinky:~]# ls -l /proc/3358/exe 
lrwxrwxrwx. 1 elcuco elcuco 0 2010-06-15 12:33 /proc/3358/exe -> /bin/bash

So your code will be:

if [ -f /proc/$PID/exe ]; then
    kill $PID 
fi

BTW: whats wrong with kill -9 $PID || true ?


EDIT:

After thinking about it for a few months.. (about 24...) the original idea I gave here is a nice hack, but highly unportable. While it teaches a few implementation details of Linux, it will fail to work on Mac, Solaris or *BSD. It may even fail on future Linux kernels. Please - use "ps" as described in other responses.

elcuco
  • 8,948
  • 9
  • 47
  • 69
8

It seems like you want

wait $PID

which will return when $pid finishes.

Otherwise you can use

ps -p $PID

to check if the process is still alive (this is more effective than kill -0 $pid because it will work even if you don't own the pid).

Gagan Gami
  • 10,121
  • 1
  • 29
  • 55
  • 4
    wait is not so effective as process should be child of current shell or it will give : `pid 123 is not a child of this shell` – Calumah Nov 22 '18 at 09:12
8

I think that is a bad solution, that opens up for race conditions. What if the process dies between your test and your call to kill? Then kill will fail. So why not just try the kill in all cases, and check its return value to find out how it went?

unwind
  • 391,730
  • 64
  • 469
  • 606
  • +1 unfortunately kill(1)'s exit code doesn't distinguish the different error situations (looks like it increments the exit value by one for each process it failed to signal). if the OP doesn't mind writing their own kill(2) wrapper, he could have it exit with different values based on the value of ERRNO after a failed kill(2) call. – just somebody Jun 15 '10 at 09:47
  • at the moment I am just doing kill -9 with no check - i just get an error "process doesn't exist" if it doesn't exist which isn't very tidy. How would I test what happened? – Richard H Jun 15 '10 at 09:51
  • 15
    Don't carelessly `kill -9`. That just instantly kills the process giving it no chance to clean up after itself. Instead use `kill` which is equivalent to `kill -15`. If that doesn't work, you should find out why, and only as a last resort use `kill -9`. – Christoffer Hammarström Jun 15 '10 at 11:08
2

For example in GNU/Linux you can use:

Pid=$(pidof `process_name`)

if [ $Pid > 0 ]; then

   do something
else

   do something
fi 

Or something like

Pin=$(ps -A | grep name | awk 'print $4}')
echo $PIN

and that shows you the name of the app, just the name without ID.

Alexis Wilke
  • 19,179
  • 10
  • 84
  • 156
inukaze
  • 441
  • 1
  • 6
  • 17
  • 1
    `pidof` does not return a negative number, as a negative PID does not make any sense, and you can't kill `init`, so your conditional makes no sense (and besides, you'd need to escape the `>` to prevent it from performing a redirection). You want to check for an empty result, but of course, like any decent tool, `pidof` sets an exit code to tell you whether it worked, so the proper solution is `if Pid=$(pidof 'process_name'); then ...` or (if you won't need the value in `Pid` later on) simply `if pidof 'process_name'; then...` – tripleee Jul 27 '16 at 09:47
  • @tripleee is right the `pidof` example is full of misunderstandings about how bash `test` works. https://www.gnu.org/software/bash/manual/html_node/Bash-Conditional-Expressions.html – Bruno Bronosky Mar 07 '18 at 19:40
1

By pid:

pgrep [pid] >/dev/null

By name:

pgrep -u [user] -x [name] >/dev/null

"-x" means "exact match".

0

here i store the PID in a file called .pid (which is kind of like /run/...) and only execute the script if not already being executed.

#!/bin/bash
if [ -f .pid ]; then
  read pid < .pid
  echo $pid
  ps -p $pid > /dev/null
  r=$?
  if [ $r -eq 0 ]; then
    echo "$pid is currently running, not executing $0 twice, exiting now..."
    exit 1
  fi
fi

echo $$ > .pid

# do things here

rm .pid

note: there is a race condition as it does not check how that pid is called. if the system is rebooted and .pid exists but is used by a different application this might lead 'unforeseen consequences'.

0

I learned from and upvoted @FDS's answer here, because it is good and correct. But, here's a form I find easier to read and understand.

So, here is my preferred version:

Explanation:

  1. The "$?" part means "the exit or return code from the previous command".
  2. The ps --pid "$pid" command returns exit code 0 if the specified PID ("$pid") is running, and some other number if not.
  3. Doing > /dev/null discards all output printed to stdout, since we don't want to see it. Rather, all we want is the exit code ("$?") from the ps --pid "$pid" command to see if that PID is running. More specifically, using > /dev/null redirects stdout output to the /dev/null pseudofile, whose purpose is to discard all incoming input.
pid=1234
ps --pid "$pid" > /dev/null
if [ "$?" -eq 0 ]; then
    echo "PID $pid exists and is running."
fi

Running shellcheck path/to/this_script.sh tells me I should actually do it the other way (as @FDS shows) to avoid redundancy. See the shellcheck output here:

eRCaGuy_hello_world/bash$ shellcheck check_if_pid_exists.sh 

In check_if_pid_exists.sh line 46:
if [ "$?" -eq 0 ]; then
     ^--^ SC2181: Check exit code directly with e.g. 'if mycmd;', not indirectly with $?.

For more information:
  https://www.shellcheck.net/wiki/SC2181 -- Check exit code directly with e.g...

Notice especially this part:

SC2181: Check exit code directly with e.g. 'if mycmd;', not indirectly with $?.

See the full description for this error code here: https://github.com/koalaman/shellcheck/wiki/SC2181.

So, shellcheck recommends this form instead:

pid=1234
if ps --pid "$pid" > /dev/null; then
    echo "PID $pid exists and is running."
fi

If you want to adjust to doing it that way, go for it. But, if you'd rather do it my way because you consider my way easier to read and understand, as I do, then you can optionally disable that shellcheck warning by adding # shellcheck disable=SC2181 above that line, like this:

My final and preferred answer

ps --pid "$pid" >/dev/null
# shellcheck disable=SC2181
if [ "$?" -eq 0 ]; then
    echo "PID $pid exists and is running."
else
    echo "PID $pid does NOT exist."
fi

The key takeaway here, however, is to never insert any command, not even an echo or print statement, between the command being checked (ps in this case) and checking the error code with "$?", or else checking the error code will accidentally check the error code from the latest command, such as the inserted echo or print statement, instead of from the command of interest (ps in our case)!

That's the main reason shellcheck recommends what they do: they want to make sure you're checking the error code of the correct command.

Other than that, it's just a pedantic opinion-based-discussion-equivalent to the argument in C about how you should check for NULL (null pointer) values:

// The way preferred by pedantic people who don't want to "be redundant" with 
// an "unnecessary" `== NULL` check:
if (!some_ptr)
{
    printf("ERROR: null ptr.\n");
    return;
}

// Versus the more-readable and understandable way which I prefer to use
// whenever my peers will approve it without a fight or long argument
if (some_ptr == NULL)       // in C
// if (some_ptr == nullptr) // in C++
{
    printf("ERROR: null ptr.\n");
    return;
}

// Note: I just want to get my code merged and move on with life, so if they
// won't easily approve my preferred version above, I'll switch to the other,
// more-pedantic version of code to try to get a quick approval so I can be
// more productive. 
// There are few things more discouraging than being blocked over
// silly "pedantry" when your code is correct, bug-free, well-written, and does
// what it says it does.

So, just as which way you choose to check for NULL values (null ptrs) in C is purely a matter of taste, which way in bash you choose to check for error codes/return codes is also purely a matter of taste. Currently, I prefer the version above I have marked as "My final and preferred answer".

Anyway, here is a full, runnable program

check_if_pid_exists.sh from my eRCaGuy_hello_world repo:

#!/usr/bin/env bash

pid=1234

if [ "$#" -gt 0 ]; then
    # At least 1 argument was passed in, so assume it is the PID
    pid="$1"
fi

# Try to print the process (`ps`) information for this PID. Send it to
# /dev/null, however, so we don't actually have to look at it. We just want
# the return code, `$?`, which will be 0 if the process exists and some other
# number if not.
ps --pid "$pid" > /dev/null
# shellcheck disable=SC2181
if [ "$?" -eq 0 ]; then
    echo "PID $pid exists and is running."
else
    echo "PID $pid does NOT exist."
fi

Sample run calls and output:

eRCaGuy_hello_world/bash$ ./check_if_pid_exists.sh 28876
PID 28876 exists and is running.

eRCaGuy_hello_world/bash$ ./check_if_pid_exists.sh
PID 1234 does NOT exist.

eRCaGuy_hello_world/bash$ ./check_if_pid_exists.sh 5678
PID 5678 does NOT exist.

You can find valid PIDs (Process IDs) to send to my script by first running ps aux and choosing a PID to pass to it.

Related

  1. My answer on How to wait in bash for several subprocesses to finish, and return exit code !=0 when any subprocess ends with code !=0?
E_net4
  • 27,810
  • 13
  • 101
  • 139
Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265