Marry Jane
Marry Jane

Reputation: 354

Check if bash script already running except itself with arguments

So I've looked up other questions and answers for this and as you can imagine, there are lots of ways to find this. However, my situation is kind of different.

I'm able to check whether a bash script is already running or not and I want to kill the script if it's already running.

The problem is that with the below code, -since I'm running this within the same script- the script kills itself too because it sees a script already running.

result=`ps aux | grep -i "myscript.sh" | grep -v "grep" | wc -l`
if [ $result -ge 1 ]
   then
        echo "script is running"
   else
        echo "script is not running"
fi

So how can I check if a script is already running besides it's own self and kill itself if there's another instance of the same script is running, else, continue without killing itself.

I thought I could combine the above code with $$ command to find the script's own PID and differentiate them this way but I'm not sure how to do that.

Also a side note, my script can be run multiple times at the same time within the same machine but with different arguments and that's fine. I only need to identify if script is already running with the same arguments.

Upvotes: 0

Views: 1636

Answers (2)

RavinderSingh13
RavinderSingh13

Reputation: 133600

I would go with either of 2 ways to solve this problem.

1st solution: Create a watchdog file lets say a .lck file kind of on a location before starting the script's execution(Make sure we use trap etc commands in case script is aborted so that .lck file should be removed) AND remove it once execution of script is completed successfully.

Example script for 1st solution: This is just an example a test one. We need to take care of interruptions in the script, lets say script got interrupted by a command or etc then we could use trap in it too, since at that time it would have not been completed but you may need to kick it off again(since last time it was not completed).

cat file.ksh
#!/bin/bash
PWD=`pwd`
watchdog_file="$PWD/script.lck"

if [[ -f "$watchdog_file" ]]
then
    echo "Please wait script is still running, exiting from script now.."
    exit 1;
else
    touch $watchdog_file
fi
while true
do
  echo "singh" > test1
done

if [[ -f "$watchdog_file" ]]
then
    rm "$watchdog_file"
fi

2nd solution: Take pid of current running shell using $$ save it in a file. Then check if that process is still running come out of script if NOT running then move on to run statements in script.

Upvotes: 1

KamilCuk
KamilCuk

Reputation: 141463

pid=$(pgrep myscript.sh | grep -x -v $$)

# filter non-existent pids
pid=$(<<<"$pid" xargs -n1 sh -c 'kill -0 "$1" 2>/dev/null && echo "$1"' --)

if [ -n "$pid" ]; then
    echo "Other script is running with pid $pid"
    echo "Killing him!"
    kill $pid
fi

pgrep lists the pids that match the name myscript.sh. From the list we filter current $$ shell with grep -v. It the result is non-empty, then you could kill the other pid.

Without the xargs, it would work, but the pgrep myscript.sh will pick up the temporary pid created for command substitution or the pipe. So the pid will never be empty and the kill will always execute complaining about the non-existent process. To do that, for each pid in pids, I check if the pid exists with kill -0. If it does, then it is outputted, effectively filtering all nonexistent pids.

You could also use a normal for loop to filter the pids:

# filter non-existent pids
pid=$(
        for i in $pid; do
                if kill -0 "$i" 2>/dev/null; then
                        echo "$i"
                fi
        done
)

Alternatively, you could use flock to lock the file and use lsof to list current open files with filtering the current one. As it is now, I think it will kill also editors that are editing the file and such. I believe the lsof output could be better filtered to accommodate this.

if [ "${FLOCKER}" != "$0" ]; then 
        pids=$(lsof -p "^$$" -- ./myscript.sh  | awk 'NR>1{print $2}')
        if [ -n "$pids" ]; then
                echo "Other processes with $(echo $pids) found. Killing them"
                kill $pids
        fi
        exec env FLOCKER="$0" flock -en "$0" "$0" "$@"
fi

Upvotes: 1

Related Questions