Check if a process is running and exit if it is already running
Asked Answered
V

2

6

I have a shell script with methods:

start(){
echo "Hello world"
}

stop(){
ps -ef|grep script.sh|grep -v grep|xargs kill
}

while [ "$1" != "" ]; do
case "$1" in
        start)
            start
            ;;
        stop)
            stop
            ;;
        *)
            echo $"Usage: $0 {start|stop}"
            exit 1
        esac
   shift
done

I am running this script using this command: ./script.sh start to call the start method.Now, I want to check if this process is already running and exit if it is already running. I have tried some solutions online but nothing worked. Someone please help. The solutions I have tried were:

if [ -f /var/tmp/script.lock ]; then
  echo "Already running. Exiting."
  exit 1
else
  touch /var/tmp/script.lock
fi
<main script code>
#some code
rm /var/tmp/script.lock

The other one is:

PID=$(ps -ef | grep script.sh|grep -v grep)

   if [ -z $PID ]; then
       echo "Process already running"
       exit   
fi

These solutions doesn't work and exit even when the process is just started.

Value answered 1/7, 2016 at 2:37 Comment(11)
And those solutions were what? BTW you already find all the running instances of your script at stop, can't you do something alike at start?Aida
@Aida I have added the solutions I have tried. Please check and let me know the solutionValue
-z $PID will be true when the process is not already running.Guillermoguilloche
Your ps | grep commands will find the script that's running the test, since it's the same name that you're looking for.Guillermoguilloche
The lock file solution should work, what was wrong with it?Guillermoguilloche
@Guillermoguilloche So, how do I actually check if a process is already running and then exit? Yeah but the lock file is also printing "Already running. Exiting." even when the process just startedValue
flock is your friend.Kailyard
Also related: #185951Kailyard
Also related: unix.stackexchange.com/questions/22044/…Kailyard
Also related: #37401905 -- I'd probably call this one the best duplicate.Kailyard
...that said, most modern operating systems will provide you a better way to control your daemons built in, including maintaining only a single instance but also adding support for things like automatically restarting them on crashes. See upstart, systemd, DJB daemontools, runit, supervisord, monit, launchd, &c. [My personal favorite is runit, but whichever your OS ships with is typically the most appropriate choice].Kailyard
G
3

The .lock file solution should work. The only problem with it is if the script exits due to an error, and doesn't remove the lock file. An improvement is to store the PID of the process in the file, and check whether that PID still exists.

if [ -f /var/tmp/script.lock ] && kill -0 $(cat /var/tmp/script.lock); then
  echo "Already running. Exiting."
  exit 1
else
  echo $$ > /var/tmp/script.lock
fi
<main script code>
#some code
rm /var/tmp/script.lock

kill -0 doesn't actually send a signal to the process, it just tests whether it's possible to send a signal: the PID exists and you have permission to send signals to it (unless you're running as root, this means the process is running with your same userid).

There's still a remote chance that this could get a false positive, if the script crashes and then its PID gets reused by the same user. But PID reuse should take a long time, and the chance that it gets reused by the same user should be low.

There's also the chance that two copies of the script will start simultaneously, and they'll both see that there's no lock file. If you need to protect against that, you should use the lockfile command to implement a mutex around the code that checks for the file.

Guillermoguilloche answered 1/7, 2016 at 3:7 Comment(10)
echo $$ >/var/tmp/script.lock isn't a completely atomic operation -- there's a window, albeit tiny, when the file exists but doesn't yet have contents.Kailyard
@CharlesDuffy Good point, I've added a caveat and reference to the lockfile command.Guillermoguilloche
nod -- saw that, and as amended this has my +1 (though I still think the question should probably be marked dupe).Kailyard
it is not working. I am trying to run ./script.sh start while it is already running and exit. But this code is not working with lock file alsoValue
Is there a reason you're not using an existing facility like systemd to deal with this, instead of rolling your own?Guillermoguilloche
When you use ./script.sh start, does it run the program in the background, so that script.sh exits?Guillermoguilloche
This code assumes that script.sh keeps running until start finishes. If script.sh exits immediately, then the script.lock file will be gone.Guillermoguilloche
...which is one of the advantages of the flock() / fcntl(F_{GET,SET}LK) lockfile approach -- if you put the lockfile on a FD inherited by your daemon, the lock is held until the daemon exits.Kailyard
@Guillermoguilloche yes I am running ./script.sh start in backgroundValue
No, I mean when script.sh starts the program, does it run the program in the background?Guillermoguilloche
Q
1

Here my script hope useful.

#!/bin/bash
export JAVA_HOME=/usr/java/jdk1.7.0_25

checkpid()
{
        echo $(ps -ef | grep "LiquidityWarning.jar" | grep -v grep | awk '{ print $2}')
}

start ()
{
    if [ $(checkpid) ] ; then
        echo -e "\n$(date +%Y%m%d-%H:%M:%S) LiquidityWarning.jar is running (pid:$(checkpid))\n"
    else
        echo ""
        printf "$(date +%Y%m%d-%H:%M:%S) LiquidityWarning.jar is starting..."
        cd /app/mservice/CBTK_new
        /usr/java/jdk1.7.0_25/bin/java -jar LiquidityWarning.jar > /dev/null 2>&1 &

    fi

}

stop ()
{
    if [ $(checkpid) ] ; then
        kill -9 $(checkpid)
        echo -e "\n$(date +%Y%m%d-%H:%M:%S) LiquidityWarning.jar stop success\n"
    fi

}

status ()
{
        if [ $(checkpid) ] ; then
        echo -e "\n$(date +%Y%m%d-%H:%M:%S) LiquidityWarning.jar is running (pid:$(checkpid))\n"
    else    
        echo -e "\n$(date +%Y%m%d-%H:%M:%S) LiquidityWarning.jar is not started\n"
    fi
}

restart()
{
        if [ $(checkpid) ] ; then
                stop
                sleep 2
                start
        else
                echo -e "\n$(date +%Y%m%d-%H:%M:%S) LiquidityWarning.jar is not started\n"
        fi
}

case "$1" in
  start)
      start
      ;;
  stop)
      stop
      ;;
  restart)
      restart
      ;;
  status)
      status
      ;;
  *)
    echo -e "\nUsage: $0 {start|stop|status|restart|reload}\n"
      exit 1
      ;;
esac
Qualm answered 1/7, 2016 at 3:31 Comment(3)
There's much that's wrong with this. The -e argument to echo should only be used with specific cause (it's actually noncompliant with the POSIX spec for echo -- see pubs.opengroup.org/onlinepubs/009604599/utilities/echo.html, which advises using printf %b instead); moreover, echo $(foo) is generally an inefficient and buggy way of running foo. This is also missing quotes in numerous places; consider running it through shellcheck.net.Kailyard
...the ps | grep approach is also dangerous in general. ps | grep LiquidityWarning.jar means you're killing not just the service, but also administrative commands like scp buildserver:/path/to/new/LiquidityWarning.jar . which happen to contain the name. Much safer to use a proper process supervision system rather than relying on name matching.Kailyard
Thanks, my script run stability now, i will improve my script.Nanananak

© 2022 - 2024 — McMap. All rights reserved.