In a bash script, I want to do the following (in pseudo-code):
if 开发者_JS百科[ a process exists with $PID ]; then
kill $PID
fi
What's the appropriate expression for the conditional statement?
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?
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
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.
ps
command with -p $PID
can do this:
$ ps -p 3531
PID TTY TIME CMD
3531 ? 00:03:07 emacs
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.
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?
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).
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.
By pid:
pgrep [pid] >/dev/null
By name:
pgrep -u [user] -x [name] >/dev/null
"-x" means "exact match".
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'.
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:
- The
"$?"
part means "the exit or return code from the previous command". - The
ps --pid "$pid"
command returns exit code0
if the specified PID ("$pid"
) is running, and some other number if not. - Doing
> /dev/null
discards all output printed tostdout
, since we don't want to see it. Rather, all we want is the exit code ("$?"
) from theps --pid "$pid"
command to see if that PID is running. More specifically, using> /dev/null
redirectsstdout
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
- 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?
精彩评论