How do I make sure my bash script isn't already running?

后端 未结 13 837
無奈伤痛
無奈伤痛 2020-12-31 05:56

I have a bash script I want to run every 5 minutes from cron... but there\'s a chance the previous run of the script isn\'t done yet... in this case, i want the new run to j

相关标签:
13条回答
  • 2020-12-31 06:33

    you can use this one:

    pgrep -f "/bin/\w*sh .*scriptname" | grep -vq $$ && exit
    
    0 讨论(0)
  • 2020-12-31 06:34

    If you use a lockfile, you should make sure that the lockfile is always removed. You can do this with 'trap':

    if ( set -o noclobber; echo "locked" > "$lockfile") 2> /dev/null; then
      trap 'rm -f "$lockfile"; exit $?' INT TERM EXIT
      echo "Locking succeeded" >&2
      rm -f "$lockfile"
    else
      echo "Lock failed - exit" >&2
      exit 1
    fi
    

    The noclobber option makes the creation of lockfile atomic, like using a directory.

    0 讨论(0)
  • 2020-12-31 06:35

    Since a socket solution has not yet been mentioned it is worth pointing out that sockets can be used as effective mutexes. Socket creation is an atomic operation, like mkdir is as Gunstick pointed out, so a socket is suitable to use as a lock or mutex.

    Tim Kay's Perl script 'Solo' is a very small and effective script to make sure only one copy of a script can be run at any one time. It was designed specifically for use with cron jobs, although it works perfectly for other tasks as well and I've used it for non-crob jobs very effectively.

    Solo has one advantage over the other techniques mentioned so far in that the check is done outside of the script you only want to run one copy of. If the script is already running then a second instance of that script will never even be started. This is as opposed to isolating a block of code inside the script which is protected by a lock. EDIT: If flock is used in a cron job, rather than from inside a script, then you can also use that to prevent a second instance of the script from starting - see example below.

    Here's an example of how you might use it with cron:

    */5 * * * * solo -port=3801 /path/to/script.sh args args args
    
    # "/path/to/script.sh args args args" is only called if no other instance of
    # "/path/to/script.sh" is running, or more accurately if the socket on port 3801
    # is not open. Distinct port numbers can be used for different programs so that
    # if script_1.sh is running it does not prevent script_2.sh from starting, I've
    # used the port range 3801 to 3810 without conflicts. For Linux non-root users 
    # the valid port range is 1024 to 65535 (0 to 1023 are reserved for root).
    
    * * * * * solo -port=3802 /path/to/script_1.sh
    * * * * * solo -port=3803 /path/to/script_2.sh
    
    # Flock can also be used in cron jobs with a distinct lock path for different
    # programs, in the example below script_3.sh will only be started if the one
    # started a minute earlier has already finished.
    
    * * * * * flock -n /tmp/path.to.lock -c /path/to/script_3.sh
    

    Links:

    • Solo web page: http://timkay.com/solo/
    • Solo script: http://timkay.com/solo/solo

    Hope this helps.

    0 讨论(0)
  • 2020-12-31 06:40

    You can use this.

    I'll just shamelessly copy-paste the solution here, as it is an answer for both questions (I would argue that it's actually a better fit for this question).

    Usage

    1. include sh_lock_functions.sh
    2. init using sh_lock_init
    3. lock using sh_acquire_lock
    4. check lock using sh_check_lock
    5. unlock using sh_remove_lock

    Script File

    sh_lock_functions.sh

    #!/bin/bash
    
    function sh_lock_init {
        sh_lock_scriptName=$(basename $0)
        sh_lock_dir="/tmp/${sh_lock_scriptName}.lock" #lock directory
        sh_lock_file="${sh_lock_dir}/lockPid.txt" #lock file
    }
    
    function sh_acquire_lock {
        if mkdir $sh_lock_dir 2>/dev/null; then #check for lock
            echo "$sh_lock_scriptName lock acquired successfully.">&2
            touch $sh_lock_file
            echo $$ > $sh_lock_file # set current pid in lockFile
            return 0
        else
            touch $sh_lock_file
            read sh_lock_lastPID < $sh_lock_file
            if [ ! -z "$sh_lock_lastPID" -a -d /proc/$sh_lock_lastPID ]; then # if lastPID is not null and a process with that pid exists
                echo "$sh_lock_scriptName is already running.">&2
                return 1
            else
                echo "$sh_lock_scriptName stopped during execution, reacquiring lock.">&2
                echo $$ > $sh_lock_file # set current pid in lockFile
                return 2
            fi
        fi
        return 0
    }
    
    function sh_check_lock {
        [[ ! -f $sh_lock_file ]] && echo "$sh_lock_scriptName lock file removed.">&2 && return 1
        read sh_lock_lastPID < $sh_lock_file
        [[ $sh_lock_lastPID -ne $$ ]] && echo "$sh_lock_scriptName lock file pid has changed.">&2  && return 2
        echo "$sh_lock_scriptName lock still in place.">&2
        return 0
    }
    
    function sh_remove_lock {
        rm -r $sh_lock_dir
    }
    

    Usage example

    sh_lock_usage_example.sh

    #!/bin/bash
    . /path/to/sh_lock_functions.sh # load sh lock functions
    
    sh_lock_init || exit $?
    
    sh_acquire_lock
    lockStatus=$?
    [[ $lockStatus -eq 1 ]] && exit $lockStatus
    [[ $lockStatus -eq 2 ]] && echo "lock is set, do some resume from crash procedures";
    
    #monitoring example
    cnt=0
    while sh_check_lock # loop while lock is in place
    do
        echo "$sh_scriptName running (pid $$)"
        sleep 1
        let cnt++
        [[ $cnt -gt 5 ]] && break
    done
    
    #remove lock when process finished
    sh_remove_lock || exit $?
    
    exit 0
    

    Features

    • Uses a combination of file, directory and process id to lock to make sure that the process is not already running
    • You can detect if the script stopped before lock removal (eg. process kill, shutdown, error etc.)
    • You can check the lock file, and use it to trigger a process shutdown when the lock is missing
    • Verbose, outputs error messages for easier debug
    0 讨论(0)
  • 2020-12-31 06:41

    If you want to check the process's existence, just look at the output of

    ps aux | grep your_script_name

    If it's there, it's not dead...

    As pointed out in the comments and other answers, using the PID stored in the lockfile is much safer and is the standard approach most apps take. I just do this because it's convenient and I almost never see the corner cases (e.g. editing the file when the cron executes) in practice.

    0 讨论(0)
  • 2020-12-31 06:42

    You might want to have a look at the man page for the flock command, if you're lucky enough to get it on your distribution.

    NAME
           flock - Manage locks from shell scripts
    SYNOPSIS
           flock [-sxon] [-w timeout] lockfile [-c] command...
    
    0 讨论(0)
提交回复
热议问题