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.

user6348718
  • 1,355
  • 5
  • 21
  • 28
  • 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`? – bipll Jul 01 '16 at 02:43
  • @bipll I have added the solutions I have tried. Please check and let me know the solution – user6348718 Jul 01 '16 at 02:50
  • `-z $PID` will be true when the process is *not* already running. – Barmar Jul 01 '16 at 02:58
  • Your `ps | grep` commands will find the script that's running the test, since it's the same name that you're looking for. – Barmar Jul 01 '16 at 03:00
  • 1
    The lock file solution should work, what was wrong with it? – Barmar Jul 01 '16 at 03:00
  • @Barmar 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 started – user6348718 Jul 01 '16 at 03:02
  • `flock` is your friend. – Charles Duffy Jul 01 '16 at 03:10
  • Also related: http://stackoverflow.com/questions/185451/quick-and-dirty-way-to-ensure-only-one-instance-of-a-shell-script-is-running-at-a – Charles Duffy Jul 01 '16 at 03:12
  • Also related: http://unix.stackexchange.com/questions/22044/correct-locking-in-shell-scripts – Charles Duffy Jul 01 '16 at 03:12
  • Also related: http://stackoverflow.com/questions/37401405/how-to-implement-singleton-in-shell-script -- I'd probably call this one the best duplicate. – Charles Duffy Jul 01 '16 at 03:13
  • ...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]. – Charles Duffy Jul 01 '16 at 03:21

2 Answers2

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.

Barmar
  • 741,623
  • 53
  • 500
  • 612
  • `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. – Charles Duffy Jul 01 '16 at 03:17
  • @CharlesDuffy Good point, I've added a caveat and reference to the `lockfile` command. – Barmar Jul 01 '16 at 03:22
  • *nod* -- saw that, and as amended this has my +1 (though I still think the question should probably be marked dupe). – Charles Duffy Jul 01 '16 at 03:23
  • 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 also – user6348718 Jul 01 '16 at 03:45
  • 1
    Is there a reason you're not using an existing facility like `systemd` to deal with this, instead of rolling your own? – Barmar Jul 01 '16 at 03:48
  • When you use `./script.sh start`, does it run the program in the background, so that `script.sh` exits? – Barmar Jul 01 '16 at 03:49
  • This code assumes that `script.sh` keeps running until `start` finishes. If `script.sh` exits immediately, then the `script.lock` file will be gone. – Barmar Jul 01 '16 at 03:50
  • ...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. – Charles Duffy Jul 01 '16 at 04:01
  • @Barmar yes I am running `./script.sh start` in background – user6348718 Jul 01 '16 at 12:12
  • No, I mean when `script.sh` starts the program, does it run the program in the background? – Barmar Jul 01 '16 at 14:53
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
  • 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 http://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 http://shellcheck.net. – Charles Duffy Jul 01 '16 at 03:41
  • ...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. – Charles Duffy Jul 01 '16 at 03:43
  • Thanks, my script run stability now, i will improve my script. – Bình Nguyễn Quang Jul 01 '16 at 03:53