My script is executed by Cron and every 2 min checks if xxx is running. If it is not in the process then the script will run it. The problem is that sometimes it runs it several times.
My problem is how to detect that the program is running several times?
How does bash detect that the pidof function returns several rather than one pid?
#!/bin/bash
PID=`pidof xxx`
if [ "$PID" = "" ];
then
cd
cd /home/pi
sudo ./xxx
echo "OK"
else
echo "program is running"
fi
You can use this script for doing the same. It will make sure script is executed once.
#!/bin/bash
ID=`ps -ef|grep scriptname|grep -v grep|wc -l`
if [ $ID -eq 0 ];
then
#run the script
else
echo "script is running"
fi
Related
I have a python script called hdsr_writer.py. I can launch this script in shell by calling
"python hdsr_writer.py 1234"
where 1234 is a parameter.
I made a shell script to increase the number and execute the python script with the number every 1 second
for param from 1 to 100000
python hdsr_writer.py $param &
sleep (1)
Usually, the python script executes its task within 0.5 second. However, there are times at which the python script gets stuck and resides in the system for longer than 30 seconds. I don't want that. So I would like to monitor life time of each python process executed. If it has stayed for longer than 2 second it would be killed and re-executed 2 times at most.
Note: I would like do this in the shell script not python script because I could not change the python script.
Update: More explainations about my question
Please note that: launching a new python process and monitoring python processes are independent jobs. Launching job doesn't care how many python processes are running and how "old" they are, just calls "python hdsr_writer.py $param &" every 1 second after increasing param. On the other hand, monitoring job periodically checks life time of all hdsr_writer python processes. If one has resided more than 2 second in memory, kills it, and re-runs it at most of 2 times.
Not so short answer
#/bin/bash
param=1
while [[ $param -lt 100000 ]]; do
echo "param=$param"
chances=3
while [[ $chances -gt 0 ]]; do
python tst.py $param &
sleep 2
if [[ "$(jobs | grep 'Running')" == "" ]]; then
chances=0
else
kill -9 $(jobs -l | awk '{print $2}')
chances=$(($chances-1))
if [[ $chances -gt 0 ]]; then
echo "one more chance for parameter $param"
fi
fi
done
param=$(($param+1))
done
UPD
This is another answer as requested by OP.
Here is still 2 scripts in one. But they can be spitted in two files.
Please pay attention that $() & is used to run sub-shells in background
#!/bin/bash
# Script launcher
pscript='rand.py'
for param in {1..10}
do
# start background sub-shell, where python with $param is started
echo $(
left=3
error_on_exit=1
# go if any chances left and previous run exits not with code 0
while [[ ( ( $left -gt 0 ) && ( $error_on_exit -ne 0 ) ) ]]; do
left=$(($left-1))
echo "param=$param; chances left $left "
# run python and grab python exit code (=0 if ok)
python $pscript $param
error_on_exit=$?
done
) &
done
# Script controller
# just kills python processes older than 2 seconds
# exits after no python left
# $(...) & can be removed if this code goes to separate script
$(while [[ $(ps | grep -v 'grep' | grep -c python ) != "0" ]]
do
sleep 0.5
killall -9 -q --older-than 2s python
done) &
Use a combination of sleep and nohup commands. After sleep time use kill to finish the execution of python script. You can check if the process is running with ps command.
#!/usr/bin/ksh
for param from {1..100000}
nohup python hdsr_writer.py $param &
pid=$!
sleep(2)
if [ ps -p $pid ]
then
kill -9 $pid
fi
done
Re-answer:
I'd use two scripts, the first one (script1.ksh):
#!/usr/bin/ksh
for param from {1..1000000}
nohup script2.sh $param &
done
And the second (script2.ksh):
#!/usr/bin/ksh
for i from {1..3}
python hsdr_write.py $1 &
pid=$!
sleep(2)
if [ ps -p $pid ]
then
kill -9 $pid
else
echo 'Finalizado'$1 >> log.txt
return
fi
done
The first script will launch all yours processes one after the other. The second one will check his own python process.
how to proceed in the script if file exists?
#!/bin/bash
echo "Start"
# waiting to be exist file
echo "file already exists, continuing"
Do a while if a sleep X, so that it will check the existence of the file every X seconds.
When the file will exist, the while will finish and you will continue with the echo "file already exists, continuining".
#!/bin/bash
echo "Start"
### waiting to be exist file
while [ ! -f "/your/file" ]; # true if /your/file does not exist
do
sleep 1
done
echo "file already exists, continuing"
And goes instead of checking the file existence check if the script
has already completed the background?
Based on the code you posted, I did some changes to make it work completely:
#!/bin/bash
(
sleep 5
) &
PID=$!
echo "the pid is $PID"
while [ ! -z "$(ps -ef | awk -v p=$PID '$2==p')" ]
do
echo "still running"
sleep 1
done
echo "done"
There are OS-specific ways to perform blocking waits on the file system. Linux uses inotify (I forget the BSD equivalent). After installing inotify-tools, you can write code similar to
#!/bin/bash
echo "Start"
inotifywait -e create $FILE & wait_pid=$!
if [[ -f $FILE ]]; then
kill $wait_pid
else
wait $wait_pid
fi
echo "file exists, continuing"
The call to inotifywait does not exit until it receives notification from the operating system that $FILE has been created.
The reason for not simply calling inotifywait and letting it block is that there is a race condition: the file might not exist when you test for it, but it could be created before you can start watching for the creation event. To fix that, we start a background process that waits for the file to be created, then check if it exists. If it does, we can kill inotifywait and proceed. If it does not, inotifywait is already watching for it, so we are guaranteed to see it be created, so we simply wait on the process to complete.
To fedorqui: Is it so good? There is a problem?
#!/bin/bash
(
..
my code
..
) &
PID=$BASHPID or PID=$$
while [ ! ps -ef | grep $PID ]
do
sleep 0
done
Thank you
I need to develop a shell script that would not be started if another instance of them self is running.
If I build a test.sh that monitors itself I need to know if it is already running and then abort, otherwise (if it not previously running) I can run
#!/bin/bash
loop() {
while [ 1 ]; do
echo "run";
#-- (... omissis ...)
sleep 30
done
}
daemon="`/bin/basename $0`"
pidlist=`/usr/bin/pgrep $daemon | grep -v $$`
echo "1:[ $pidlist ]"
pidlist=$(/usr/bin/pgrep $daemon | grep -v $$)
echo "2:[ $pidlist ]"
echo "3:[ `/usr/bin/pgrep $daemon | grep -v $$` ]"
echo "4:["
/usr/bin/pgrep $daemon | grep -v $$
echo "]"
if [ -z "$pidlist" ]; then
loop &
else
echo "Process $daemon is already running with pid [ $pidlist ]"
fi
exit 0;
When I run the above script for the first time (no previous instances running) I get this output:
1:[ 20341 ]
2:[ 20344 ]
3:[ 20347 ]
4:[
]
I cannot understand why only 4th attempt does not return anything (as expected). What's wrong in my script?
Do I have to redirect output of 4th command on a temporary file and then query that file in order to decide if I can run (or not) the loop function?
Thanks anyone would help me!
Sub-shells...the first three are run in sub-shells and hence $$ has changed to the PID of the sub-shell.
Try using:
PID=$$
pidlist=`/usr/bin/pgrep $daemon | grep -v $PID`
echo "1:[ $pidlist ]"
Etc. Since the value of $PID is established before the sub-shell is run, it should be the same for all of the commands.
Is this process going to be popular enough that other people want to run the same daemon on the machine? Maybe you never have multiple users on the machine, but remember that someone else might be wanting to run the command too.
I wrote a bash script that starts a number of different widgets (various Rails applications) and runs them in the background. I'm now trying to write a complimenting stop script that kills each of the processes started by that start script, but I'm not sure of the best way to approach it.
Following is my start script:
#!/bin/bash
widgets=( widget1 widget2 widget3 ) # Specifies, in order, which widgets to load
port=3000
basePath=$("pwd")
for dir in "${widgets[#]}"
do
cd ${basePath}/widgets/$dir
echo "Starting ${dir} widget."
rails s -p$port &
port=$((port+1))
done
If possible, I was trying to avoid saving the PIDs to a .pid file because they're horribly unreliable. Is there a better way to approach this?
One possibility is to use pkill with the -f switch that is described thus in the man page:
-f The pattern is normally only matched against the process name. When -f is set, the full command line is used.
Hence, if you want to kill rails s -p3002, you can proceed as follows:
pkill -f 'rails s -p3002'
To keep extra dependencies at a minimum and ensure I didn't shut down rails instances that don't belong to me, I ended up going with the following:
Start Script
#!/bin/bash
widgets=( widget1 widget2 widget3 ) # Specifies, in order, which widgets to load
port=3000
basePath=$("pwd")
pidFile="${basePath}/pids.pid"
if [ -f $pidFile ];
then
echo "$pidFile already exists. Stop the process before attempting to start."
else
echo -n "" > $pidFile
for dir in "${widgets[#]}"
do
cd ${basePath}/widgets/$dir
echo "Starting ${dir} widget."
rails s -p$port &
echo -n "$! " >> $pidFile
port=$((port+1))
done
fi
Stop Script
#!/bin/bash
pidFile='pids.pid'
if [ -f $pidFile ];
then
pids=`cat ${pidFile}`
for pid in "${pids[#]}"
do
kill $pid
done
rm $pidFile
else
echo "Process file wasn't found. Aborting..."
fi
I have a script that runs every 15 minutes but sometimes if the box is busy it hangs and the next process will start before the first one is finished creating a snowball effect. How can I add a couple lines to the bash script to check to see if something is running first before starting?
You can use pidof -x if you know the process name, or kill -0 if you know the PID.
Example:
if pidof -x vim > /dev/null
then
echo "Vim already running"
exit 1
fi
Why don't set a lock file ?
Something like
yourapp.lock
Just remove it when you process is finished, and check for it before to launch it.
It could be done using
if [ -f yourapp.lock ]; then
echo "The process is already launched, please wait..."
fi
In lieu of pidfiles, as long as your script has a uniquely identifiable name you can do something like this:
#!/bin/bash
COMMAND=$0
# exit if I am already running
RUNNING=`ps --no-headers -C${COMMAND} | wc -l`
if [ ${RUNNING} -gt 1 ]; then
echo "Previous ${COMMAND} is still running."
exit 1
fi
... rest of script ...
pgrep -f yourscript >/dev/null && exit
This is how I do it in one of my cron jobs
lockfile=~/myproc.lock
minutes=60
if [ -f "$lockfile" ]
then
filestr=`find $lockfile -mmin +$minutes -print`
if [ "$filestr" = "" ]; then
echo "Lockfile is not older than $minutes minutes! Another $0 running. Exiting ..."
exit 1
else
echo "Lockfile is older than $minutes minutes, ignoring it!"
rm $lockfile
fi
fi
echo "Creating lockfile $lockfile"
touch $lockfile
and delete the lock file at the end of the script
echo "Removing lock $lockfile ..."
rm $lockfile
For a method that does not suffer from parsing bugs and race conditions, check out:
BashFAQ/045 - How can I ensure that only one instance of a script is running at a time (mutual exclusion)?
I had recently the same question and found from above that kill -0 is best for my case:
echo "Starting process..."
run-process > $OUTPUT &
pid=$!
echo "Process started pid=$pid"
while true; do
kill -0 $pid 2> /dev/null || { echo "Process exit detected"; break; }
sleep 1
done
echo "Done."
To expand on what #bgy says, the safe atomic way to create a lock file if it doesn't exist yet, and fail if it doesn't, is to create a temp file, then hard link it to the standard lock file. This protects against another process creating the file in between you testing for it and you creating it.
Here is the lock file code from my hourly backup script:
echo $$ > /tmp/lock.$$
if ! ln /tmp/lock.$$ /tmp/lock ; then
echo "previous backup in process"
rm /tmp/lock.$$
exit
fi
Don't forget to delete both the lock file and the temp file when you're done, even if you exit early through an error.
Use this script:
FILE="/tmp/my_file"
if [ -f "$FILE" ]; then
echo "Still running"
exit
fi
trap EXIT "rm -f $FILE"
touch $FILE
...script here...
This script will create a file and remove it on exit.