Bash: How to capture a real process name and prevent displaying the command process? - bash

I wrote the next script:
process="$1"
if [ -z "$1" ]; then
echo "Please specify a process to check"
exit 1
fi
ps -ef | grep "$process" | grep -v {grep\|$(basename $0)} > /tmp/procs
if [ "$?" -eq "0" ]; then
stat="OK"
exitcode="0"
msg="Process $process is running"
else
stat="Critical"
exitcode="2"
msg="There are currently no running processes of $process"
fi
echo "$stat: $msg"
exit $exitcode
The script is supposed to check if a given process is running or not and output a relevant message.
In the script, I'm using the following command to find if the process is running:
ps -ef | grep $process
The problem is that while the script is running, the process name also includes the $process word and thus, it finds both the real process and the process of the script (as the $process is mentioned in the command).
Example:
[root#pnmnjs2 ~]# sh -x check_proc_nodeJS.sh dakjdhak
+ process=dakjdhak
+ '[' -z dakjdhak ']'
+ ps -ef
++ basename check_proc_nodeJS.sh
+ grep -v '{grep|check_proc_nodeJS.sh}'
+ grep dakjdhak
+ '[' 0 -eq 0 ']'
+ stat=OK
+ exitcode=0
+ msg='Process dakjdhak is running'
+ echo 'OK: Process dakjdhak is running'
OK: Process dakjdhak is running
+ exit 0
[root#pnmnjs2 ~]#
Meaning: There is no real process called "dakjdhak" but when the script is running the ps -ef command it also catches the process of the running script and then returns that the process exists and running... which is wrong.
How can I "catch" only the relevant process without the process of the running script?
(the addition of command | grep -v {grep\|$(basename $0)} should have done it.. but it doesnt...
Thanks in advance

Rather than piping the output of ps to grep, you can use pgrep:
# change this
# ps -ef | grep "$process" | grep -v {grep\|$(basename $0)} > /tmp/procs
# if [ "$?" -eq "0" ]; then
# to this
if pgrep "$process" > /tmp/procs; then
If you want to know the return value of a command, there's no need to use $? with [ - just test the command directly.

A little pointer on using your process swtiches
ps -ef
the -e switch will show all running processes this includes when you instantly use the pipe command
grep "$process" | grep -v {grep\|$(basename $0)}
As a result you will never get an error
example
ps -ef|grep "xxx" # if xxx is not running
output (your script)
OK: Process xxxxxx is running
you just executed the process grep which ran (it does not matter if grep was successful to the process command using -ef argument). The process command see grep ran and as a result no error was caught.
What you can use is
ps -A or ps -C

You could use ps -C "$process" to find a process using its executable name (comm, not args).

Related

Command executes fine in terminal, not in a bash script

I'm trying:
#!/bin/bash
if $(ps -C "bm_d21_debug")
then
kill $(ps -C "bm_d21_debug" -o pid=)
echo "exists"
fi
It returns: "PID: command not found"
Not sure what I'm doing wrong?
Consider this line:
if $(ps -C "bm_d21_debug")
You execute the ps command in a command substitution, which returns the command output. The if command then tries to run that output as a command.
The first word of the ps output is PID, which if will handle as the command name. Thus, the "command not found" error.
You just want
if ps -C "bm_d21_debug" >/dev/null; then
echo running
else
echo NOT running
fi
I suggest to use square brackets also:
if [[ $(ps -C "bm_d21_debug") ]]
But this command will always return "yes" ($? = 0)
Fixed by changing to
if ps aux | grep ./bm_d21_debug | grep -v grep >/dev/null;then
pid=$(ps aux | grep ./bm_d21_debug | grep -v grep | awk '{print $2}')
kill $pid
echo $pid
fi

how stopped script while procces id exist? Solaris

I want to stall the execution of my script until a process is closed (I have the PID stored in a variable).
#!/bin/bash
outputl=$( ps -ef | grep $var4 | awk '{print $2}' ) >> $logfile
while [ "ps -p $outputl" ] > /dev/null;
do
sleep 1;
done
echo "Stopped $instance" >> $logfile
//command...
It stays in the "while" and not continue whit script.
This line:
while [ "ps -p $output1" ]
does not execute the ps command. It simply tests whether the string "ps -p $output1" is not empty, and it obviously isn't. To test the output of a command, use $():
while [ "$(ps -p "$output1")" ]
But since ps produces a header, this will always be true. The best way to test if a PID exists is to use the kill command with signal 0; this doesn't actually send a signal, it just tests whether it's possible to send a signal. I'm assuming this code is being run either by root or the userid running the application being checked. So you can write:
while kill -0 "$output1" 2>/dev/null
Also, your code for getting the PID into $output1 is wrong. ps -ef will also include the grep command, which matches the name you're looking for, so you need to filter that out. Use:
output1=$(ps -ef | grep "$var4" | awk '!/grep/ { print $2 }')
Redirecting the output to $logfile is not necessary, since variable assignments don't print anything.
Many systems have a pgrep command, which can be used by itself to test if a process with a given name exists; if you have this, you can use it instead of reinventing the wheel (and if not, you should be able to install it).
If you have the PID then just wait for it to complete. Try:
outputl=$( ps -ef | awk -v v="$var4" '$0~v{print $2}' )
wait "$outputl"
echo "Stopped $instance" >> $logfile
then look for a better way to find the pid in the first line.

Bash script not killing all PIDs in specified file or allowing partial names for input [duplicate]

This question already has answers here:
How to kill all processes with a given partial name? [closed]
(14 answers)
Closed 6 years ago.
Right now, my bash script works for 1 PID processes and I must use an exact process name for input. It will not accept *firefox*' for example. Also, I run a bash script that opens multiplersync` processes, and I would like this script to kill all of those processes. But, this script only works on processes with 1 PID.
Here is the script:
#!/bin/bash
createProcfile() {
ps -eLf | grep -f process.tmp | grep -v 'grep' | awk '{print $2,$10}' | sort -u | egrep -o '[0-9]{4,}' > pid.tmp
# pgrep "$(cat process.tmp)" > pid.tmp
}
PIDFile=pid.tmp
echo "Enter a process name"
read -r process
echo "$process" > process.tmp
# node_process_id=$(pidof "$process")
node_process_id=$(ps -eLf | grep $process | grep -v 'grep' | awk '{print $2,$10}' | sort -u | egrep -o '[0-9]{4,}')
if [[ -z "$node_process_id" ]]; then
echo "Please enter a valid process."
rm process.tmp
exit 0
fi
ps -eLf | grep $process | awk '{print $2,$10}' | sort -u | grep -v 'grep'
# pgrep "$(cat process.tmp)"
echo "Would you like to kill this process(es)? (y/n)"
read -r answer
if [[ "$answer" == y ]]; then
createProcfile
pkill -F "$PIDFile"
rm "$PIDFile"
sleep 1
createProcfile
node_process_id=$(pidof "$process")
if [[ -z $node_process_id ]]; then
echo "Process terminated successfully."
rm process.tmp
exit 0
else
echo "Process not terminated. Kill process manually."
ps -eLf | grep $process | awk '{print $2,$10}' | sort -u | grep -v 'grep'
# pgrep "$(cat process.tmp)"
rm "$PIDFile"
rm process.tmp
exit 0
fi
fi
I edited the script. Thanks to your comments, it works now and does the following:
Make script accept partial name as input
Kill more than 1 PID
Thank you!
pkill exists to solve your problem. It accepts a pattern to match against the process name, or the entire command line if -f is specified.
It will not accept *firefox*
Use killall command. Example :
killall -r "process.*"
This will kill all the processes whose names contain process in the beginning followed by any stuff.
The [ manual ] says :
-r, --regexp
Interpret process name pattern as an extended regular expression.
Sidenote:
Note that we have to double quote the regular expression to prevent file globbing. (Thanks #broslow for reminding this stuff).

In unix how to find out if process running and return true/false?

I'm writing a unix shell script and need to check if there are currently running processes with "xyz" in their directory. If yes than continue to next command and show text like "Found It".
If not than don't continue and display text like "Process Not Found".
I tried something like this:
if ps -ef | grep xyz
then
echo "XYZ Process Found!"
else
echo "XYZ Process Not Found!"
fi
But it just showing me the processes and display "process found" even if there's no xyz process.
I believe you want to check the output of the command against a value using Command substition, from the linked bash-hackers wiki The command substitution expands to the output of commands. These commands are executed in a subshell, and their stdout data is what the substitution syntax expands to. Also, count the lines and remove grep. Something like,
if [[ $(ps -ef | grep xyz | grep -v grep | wc -l) != 0 ]]; then
echo "XYZ Process Found!"
else
echo "XYZ Process Not Found!"
fi
Edit
Based on the comments below, you should probably use
if [[ $(ps -ef | grep -c xyz) -ne 1 ]]; then
which is a lot easier to read.
When you run grep xyz, that process - grep xyz - is also running & thus shown in the output of ps -ef.
This running process command line contains xyz. Thus grep passes that line to output.
Hence you always get zero exit status - i.e. success.
2 Solutions:
use if ps -ef | grep '[x]yz'; then. (You may want to suppress grep output with -q)
The grep command being run is grep [x]yz. This gets printed in ps -ef output.
Obviously, grep filters out this line. [x]yz could be matched with \[x\]yz, not with [x]yz.
use if pgrep -f xyz >/dev/null; then
Check man pgrep for more details..
You can also use pgrep. From pgrep(1):
pgrep looks through the currently running processes and lists the
process IDs which match the selection criteria to stdout.
[...]
EXIT STATUS
0 One or more processes matched the criteria.
1 No processes matched.
2 Syntax error in the command line.
3 Fatal error: out of memory etc.
Example output:
[~]% pgrep xterm
18231
19070
31727
You can use it in an if statement like so:
if pgrep xterm > /dev/null; then
echo Found xterm
else
echo xterm not found
fi
Note: pgrep is not a standard utility (ie. it's not in POSIX), but widely available on at least Linux and I believe most BSD systems.
is_xyz_running() {
[ "$(pgrep xyz)" ] && echo true || echo false
}

How to check if another instance of my shell script is running

GNU bash, version 1.14.7(1)
I have a script is called "abc.sh"
I have to check this from abc.sh script only...
inside it I have written following statement
status=`ps -efww | grep -w "abc.sh" | grep -v grep | grep -v $$ | awk '{ print $2 }'`
if [ ! -z "$status" ]; then
echo "[`date`] : abc.sh : Process is already running"
exit 1;
fi
I know it's wrong because every time it exits as it found its own process in 'ps'
how to solve it?
how can I check that script is already running or not from that script only ?
An easier way to check for a process already executing is the pidof command.
if pidof -x "abc.sh" >/dev/null; then
echo "Process already running"
fi
Alternatively, have your script create a PID file when it executes. It's then a simple exercise of checking for the presence of the PID file to determine if the process is already running.
#!/bin/bash
# abc.sh
mypidfile=/var/run/abc.sh.pid
# Could add check for existence of mypidfile here if interlock is
# needed in the shell script itself.
# Ensure PID file is removed on program exit.
trap "rm -f -- '$mypidfile'" EXIT
# Create a file with current PID to indicate that process is running.
echo $$ > "$mypidfile"
...
Update:
The question has now changed to check from the script itself. In this case, we would expect to always see at least one abc.sh running. If there is more than one abc.sh, then we know that process is still running. I'd still suggest use of the pidof command which would return 2 PIDs if the process was already running. You could use grep to filter out the current PID, loop in the shell or even revert to just counting PIDs with wc to detect multiple processes.
Here's an example:
#!/bin/bash
for pid in $(pidof -x abc.sh); do
if [ $pid != $$ ]; then
echo "[$(date)] : abc.sh : Process is already running with PID $pid"
exit 1
fi
done
I you want the "pidof" method, here is the trick:
if pidof -o %PPID -x "abc.sh">/dev/null; then
echo "Process already running"
fi
Where the -o %PPID parameter tells to omit the pid of the calling shell or shell script. More info in the pidof man page.
Here's one trick you'll see in various places:
status=`ps -efww | grep -w "[a]bc.sh" | awk -vpid=$$ '$2 != pid { print $2 }'`
if [ ! -z "$status" ]; then
echo "[`date`] : abc.sh : Process is already running"
exit 1;
fi
The brackets around the [a] (or pick a different letter) prevent grep from finding itself. This makes the grep -v grep bit unnecessary. I also removed the grep -v $$ and fixed the awk part to accomplish the same thing.
Working solution:
if [[ `pgrep -f $0` != "$$" ]]; then
echo "Another instance of shell already exist! Exiting"
exit
fi
Edit: I checked out some comments lately, so I tried attempting same with some debugging. I will also will explain it.
Explanation:
$0 gives filename of your running script.
$$ gives PID of your running script.
pgrep searches for process by name and returns PID.
pgrep -f $0 searches by filename, $0 being the current bash script filename and returns its PID.
So, pgrep checks if your script PID ($0) is equal to current running script ($$). If yes, then the script runs normally. If no, that means there's another PID with same filename running, so it exits. The reason I used pgrep -f $0 instead of pgrep bash is that you could have multiple instances of bash running and thus returns multiple PIDs. By filename, its returns only single PID.
Exceptions:
Use bash script.sh not ./script.sh as it doesn't work unless you have shebang.
Fix: Use #!/bin/bash shebang at beginning.
The reason sudo doesn't work is that it returns pgrep returns PID of both bash and sudo, instead of returning of of bash.
Fix:
#!/bin/bash
pseudopid="`pgrep -f $0 -l`"
actualpid="$(echo "$pseudopid" | grep -v 'sudo' | awk -F ' ' '{print $1}')"
if [[ `echo $actualpid` != "$$" ]]; then
echo "Another instance of shell already exist! Exiting"
exit
fi
while true
do
echo "Running"
sleep 100
done
The script exits even if the script isn't running. That is because there's another process having that same filename. Try doing vim script.sh then running bash script.sh, it'll fail because of vim being opened with same filename
Fix: Use unique filename.
Someone please shoot me down if I'm wrong here
I understand that the mkdir operation is atomic, so you could create a lock directory
#!/bin/sh
lockdir=/tmp/AXgqg0lsoeykp9L9NZjIuaqvu7ANILL4foeqzpJcTs3YkwtiJ0
mkdir $lockdir || {
echo "lock directory exists. exiting"
exit 1
}
# take pains to remove lock directory when script terminates
trap "rmdir $lockdir" EXIT INT KILL TERM
# rest of script here
Here's how I do it in a bash script:
if ps ax | grep $0 | grep -v $$ | grep bash | grep -v grep
then
echo "The script is already running."
exit 1
fi
This allows me to use this snippet for any bash script. I needed to grep bash because when using with cron, it creates another process that executes it using /bin/sh.
I find the answer from #Austin Phillips is spot on. One small improvement I'd do is to add -o (to ignore the pid of the script itself) and match for the script with basename (ie same code can be put into any script):
if pidof -x "`basename $0`" -o $$ >/dev/null; then
echo "Process already running"
fi
pidof wasn't working for me so I searched some more and came across pgrep
for pid in $(pgrep -f my_script.sh); do
if [ $pid != $$ ]; then
echo "[$(date)] : my_script.sh : Process is already running with PID $pid"
exit 1
else
echo "Running with PID $pid"
fi
done
Taken in part from answers above and https://askubuntu.com/a/803106/802276
Use the PS command in a little different way to ignore child process as well:
ps -eaf | grep -v grep | grep $PROCESS | grep -v $$
I create a temporary file during execution.
This is how I do it:
#!/bin/sh
# check if lock file exists
if [ -e /tmp/script.lock ]; then
echo "script is already running"
else
# create a lock file
touch /tmp/script.lock
echo "run script..."
#remove lock file
rm /tmp/script.lock
fi
I have found that using backticks to capture command output into a variable, adversly, yeilds one too many ps aux results, e.g. for a single running instance of abc.sh:
ps aux | grep -w "abc.sh" | grep -v grep | wc -l
returns "1". However,
count=`ps aux | grep -w "abc.sh" | grep -v grep | wc -l`
echo $count
returns "2"
Seems like using the backtick construction somehow temporarily creates another process. Could be the reason why the topicstarter could not make this work. Just need to decrement the $count var.
I didn't want to hardcode abc.sh in the check, so I used the following:
MY_SCRIPT_NAME=`basename "$0"`
if pidof -o %PPID -x $MY_SCRIPT_NAME > /dev/null; then
echo "$MY_SCRIPT_NAME already running; exiting"
exit 1
fi
This is compact and universal
# exit if another instance of this script is running
for pid in $(pidof -x `basename $0`); do
[ $pid != $$ ] && { exit 1; }
done
The cleanest fastest way:
processAlreadyRunning () {
process="$(basename "${0}")"
pidof -x "${process}" -o $$ &>/dev/null
}
For other variants (like AIX) that don't have pidof or pgrep. Reliability is greatly improved by getting a "static" view of the process table as opposed to piping it directly to grep. Setting IFS to null will preserve the carriage returns when the ps output is assigned to a variable.
#!/bin/ksh93
IFS=""
script_name=$(basename $0)
PSOUT="$(ps ax)"
ANY_TEXT=$(echo $PSOUT | grep $script_name | grep -vw $$ | grep $(basename $SHELL))
if [[ $ANY_TEXT ]]; then
echo "Process is already running"
echo "$ANY_TEXT"
exit
fi
[ "$(pidof -x $(basename $0))" != $$ ] && exit
https://github.com/x-zhao/exit-if-bash-script-already-running/blob/master/script.sh

Resources