i have some Watcher.sh script running on a directory /foo/bar. When something happens in /foo/bar it starts another script (Updater.sh) which first sleeps for 30 seconds and then do stuff. The Watcher.sh script is managed by supervisor, which is configured to restart the script if it ends. So when there was a change in /foo/bar and the Updater.sh is started (in a subprocess), the Watcher script should end and get started again by supervisor. If there is a second change in /foo/bar the new Updater.sh script should end the first one and start again its 30 second sleep before doing stuff.
Watcher.sh script:
#!/usr/bin/env bash
echo "Started inotifywait with $BASHPID ID"
inotifywait -q -e modify,move,create,delete /foo/bar
/updater.sh &
Updater.sh script:
#!/usr/bin/env bash
echo "Start Update process $BASHPID"
FILE=/var/run/updater.pid
if test -f "$FILE" ; then
kill -9 `cat /var/run/updater.pid`
rm /var/run/updater.pid
fi
echo $BASHPID > /var/run/updater.pid
echo "Change in /foo/bar, reload soon"
sleep 30
supervisorctl reload
My problem is that it just don't happen. The Watcher.sh script is restarted, but the Updater.sh script is not killed at all. I checked the /var/run/updater.pid which is definitely set after the first change, but it just don't enter the if case and even when it does, it does not continue after the kill.
Would be great if somebody knows where i do something wrong, it should be simple right?..
EDIT 1:
Use the following code in the updater.sh to not listen on a pid file
if ps -ef | grep -vE "grep|$BASHPID" | grep updater | awk '{print $2}' ; then
echo "Kill it"
kill -9 `ps -ef | grep -vE "grep|$BASHPID" | grep updater | awk '{print $2}'`
fi
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.
I write a mac application. I call a shell script to do some tasks in my mac app. when shell script tasks complete, I want to kill my mac app and reopen it.
I can pgrep [app_name] | xargs kill -9 in shell script. but it does not work.
When I execute this command in my terminal it works. How can I kill my mac app in my shell script ? Hope you can help.
Try doing:
/usr/bin/pkill -9 <app name>
Failing that, and if the app spawns multiple prcesses:
for PID in $(/usr/bin/pgrep <app name>); do /bin/kill -9 $PID; done
If the process is running as root, you'll need to run the script as root or with sudo.
If you're still struggling do the following to help you debug:
#!/bin/bash
APP='<appname>'
LOG= ~/debugkill.log
PIDS: `/usr/bin/pgrep $APP`
echo -e "`/bin/date`: Script running as user: `/usr/bin/whoami` \n
Process to be killed owned by: `ps u $APP | /usr/bin/awk { print $1 }'`" >> $LOG
echo -e "PIDs to terminate: $PIDS >> $LOG
echo -e "Killing PIDs..."
for PID in $PIDS; do
/bin/kill -9 $PID
if [[ `ps $PID | grep $PID | grep -v grep | awk '{ print $1 }'` == "$PID" ]]; then
echo 'killing with sudo (requires pass)'
sudo /bin/kill -9 $PID
fi
if [[ `ps $PID | grep $PID | grep -v grep | awk '{ print $1 }'` == "$PID" ]]; then
echo 'killing with killall -9'
/usr/bin/killall -9 $APP
fi
done >> $LOG
I've had issues before with not being killed from scripts and it turned out that the binary paths weren't specified explicitly for either pkill or kill - and because they are in different folders sometimes one works and the other doesn't depending on which environmental variables are at play when running it.
To relaunch your app, you need to be clear on what app it is, whether it's launched by a binary or launched by java or something, and whether any config parameters are specified when you check the process for it whilst running i.e. run 'ps ax ' and see if anything is running
like so:
/usr/sbin/xinetd -dontfork -pidfile /var/run/xinetd.pid
..as when you relaunch you will need to include the same so you'd do something like this in the script:
/usr/sbin/xinetd -dontfork -pidfile /var/run/xinetd.pid > /dev/null 2>&1
..the other option is to turn the app into a daemon via launchd and then you can simply stop and restart\stop\start the process:
http://blog.frd.mn/how-to-set-up-proper-startstop-services-ubuntu-debian-mac-windows/
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
Here is my shell script:
#!/bin/bash
PIDS=$(ps -e | grep $1 |grep -v grep| awk '{print $1}')
kill -s SIGINT $PIDS
echo "Done sendings signal"
I am passing the name of the process as command line argument.
The echo command is not getting executed, although the target processes are actually receiving the SIGINT signal and exited.
Any suggestions?
Update:
I changed the code to:
#!/bin/bash
PIDS=$(ps -e |grep $1 | grep -v grep | awk '{print $1}'|grep -v $$)
echo $PIDS
kill -s SIGINT $PIDS
echo "Done sendings signal"
echo "The current process is $$"
Now I am noticing a strange thing:
The script is working but not as expected. Executing following command in command line outside the script
ps -e|grep process-name|grep -v grep|awk '{print $1}'|grep -v $$
gives pid of the process-name but when I execute the same command inside shell script, assign it to variable PIDS and then echo PIDS then it shows one more pid in addition to the pid of process-name. Therefore when the kill command executes it gives an error that the process with second pid doesn't exist. It does echo the remaining sentences in the terminal. Any clue ?
There really are only a couple of possibilities. Assuming you're just running this from the command line, you should see the message ... unless, of course, what you're doing puts the PID of your shell process in PIDS, in which case the kill would kill the (sub) shell running your command before you hit the echo.
Suggestion: echo $PIDS before you call kill and see what's there. In fact, I'd be tempted to comment out the kill and try the command, just to see what happens.
#!/bin/bash
PIDS=$(ps -e | grep $1 |grep -v grep| awk '{print $1}')
echo $PIDS
# kill -s SIGINT $PIDS
echo "Done sendings signal"
Of course, you can always run the script with bash -x to see everything.
Your script works. The only reason I can see for the echo not being executed is that some value of $1 and the script file name combine so that your script PID is also gathered, thereby making the script suicide.
The PIDS line spawns a process running ps, grep, another grep -- so you won't find in PIDS the processes running grep, but what about the parent process itself?
Try:
#!/bin/bash
PIDS=$(ps -e | grep $1 |grep -v grep | awk '{print $1}' | grep -v "^$$\$" )
kill -s SIGINT $PIDS
echo "Done sendings signal"
or run the pipes one after the other with suitable safety greps.
Edit: it is evident that the "$1" selection is selecting too much. So I'd rewrite the script like this:
#!/bin/bash
# Gather the output of "ps -e". This will also gather the PIDs of this
# process and of ps process and its subshell.
PSS=$( ps -e )
# Extract PIDs, excluding this one PID and excluding a process called "ps".
# Don't need to expunge 'grep' since no grep was running when getting PSS.
PIDS=$( echo "$PSS" | grep -v "\<ps\>" | grep "$1" | awk '{print $1}' | grep -v "^$$\$" )
if [ -n "$PIDS" ]; then
kill -s SIGINT $PIDS
else
echo "No process found matching $1"
fi
echo "Done sending signal."
ps -e is identical to ps -A and selects all processes ( cf. http://linux.die.net/man/1/ps ), i. e. ps -e displays "information about other users' processes, including those without controlling terminals" (Mac OS X man page of ps). This means you will also kill the PID ($$) of your shell process, as already pointed out by Charlie Martin, because you will also grep a line of output of the ps -e command that looks like so:
67988 ttys000 0:00.00 /bin/bash ./killpids sleep
Just log the output of ps -e to a file to see that your script commits suicide:
./killpids sleep 2>err.log
#!/bin/bash
# cat killpids
echo $$
for n in {1..10}; do
sleep 5000 &
done
sleep 1
unset PIDS
PIDS="$(ps -e | tee /dev/stderr | grep "$1" | grep -v grep | awk '{print $1}')"
#PIDS="$(ps -www -U $USER -o pid,uid,comm | tee /dev/stderr | grep "$1" | grep -v grep | awk '{print $1}')"
wc -l <<<"$PIDS"
#kill -s SIGINT $PIDS
echo kill -s TERM $PIDS
kill -s TERM $PIDS
echo "Done sendings signal"