How to make sure only one instance of a Bash script is running at a time?

前端 未结 6 1868
时光取名叫无心
时光取名叫无心 2021-02-08 00:54

I want to make a sh script that will only run at most once at any point.

Say, if I exec the script then I go to exec the script again, how do I make it so that if the f

相关标签:
6条回答
  • 2021-02-08 01:11

    I just wrote a tool that does this: https://github.com/ORESoftware/quicklock

    writing a good one takes about 15 loc, so not something you want to include in every shell script.

    basically works like this:

    $ ql_acquire_lock
    

    the above calls this bash function:

    function ql_acquire_lock {
      set -e;
      name="${1:-$PWD}"  # the lock name is the first argument, if that is empty, then set the lockname to $PWD
      mkdir -p "$HOME/.quicklock/locks"
      fle=$(echo "${name}" | tr "/" _)
      qln="$HOME/.quicklock/locks/${fle}.lock"
      mkdir "${qln}" &> /dev/null || { echo "${ql_magenta}quicklock: could not acquire lock with name '${qln}'${ql_no_color}."; exit 1; }
      export quicklock_name="${qln}";  # export the var *only if* above mkdir command succeeds
      trap on_ql_trap EXIT;
    }
    

    when the script exits, it automatically releases the lock using trap

    function on_ql_trap {
       echo "quicklock: process with pid $$ was trapped.";
       ql_release_lock
    }
    

    to manually release the lock at will, use ql_release_lock:

    function ql_maybe_fail {
      if [[ "$1" == "true" ]]; then
          echo -e "${ql_magenta}quicklock: exiting with 1 since fail flag was set for your 'ql_release_lock' command.${ql_no_color}"
          exit 1;
      fi
    }
    
    function ql_release_lock () {
    
       if [[ -z "${quicklock_name}" ]]; then
         echo -e "quicklock: no lockname was defined. (\$quicklock_name was not set).";
         ql_maybe_fail "$1";
         return 0;
       fi
    
       if [[ "$HOME" == "${quicklock_name}" ]]; then
         echo -e "quicklock: dangerous value set for \$quicklock_name variable..was equal to user home directory, not good.";
         ql_maybe_fail "$1"
         return 0;
       fi
    
       rm -r "${quicklock_name}" &> /dev/null &&
       { echo -e "quicklock: lock with name '${quicklock_name}' was released.";  } ||
       { echo -e "quicklock: no lock existed for lockname '${quicklock_name}'."; ql_maybe_fail "$1"; }
       trap - EXIT  # clear/unset trap
    
    }
    
    0 讨论(0)
  • 2021-02-08 01:12

    You want a pid file, maybe something like this:

    pidfile=/path/to/pidfile
    if [ -f "$pidfile" ] && kill -0 `cat $pidfile` 2>/dev/null; then
        echo still running
        exit 1
    fi  
    echo $$ > $pidfile
    
    0 讨论(0)
  • 2021-02-08 01:13

    I think you need to use lockfile command. See using lockfiles in shell scripts (BASH) or http://www.davidpashley.com/articles/writing-robust-shell-scripts.html.

    The second article uses "hand-made lock file" and shows how to catch script termination & releasing the lock; although using lockfile -l <timeout seconds> will probably be a good enough alternative for most cases.

    Example of usage without timeout:

    lockfile script.lock
    <do some stuff>
    rm -f script.lock
    

    Will ensure that any second script started during this one will wait indefinitely for the file to be removed before proceeding.

    If we know that the script should not run more than X seconds, and the script.lock is still there, that probably means previous instance of the script was killed before it removed script.lock. In that case we can tell lockfile to force re-create the lock after a timeout (X = 10 below):

    lockfile -l 10 /tmp/mylockfile
    <do some stuff>
    rm -f /tmp/mylockfile
    

    Since lockfile can create multiple lock files, there is a parameter to guide it how long it should wait before retrying to acquire the next file it needs (-<sleep before retry, seconds> and -r <number of retries>). There is also a parameter -s <suspend seconds> for wait time when the lock has been removed by force (which kind of complements the timeout used to wait before force-breaking the lock).

    0 讨论(0)
  • 2021-02-08 01:15

    Write the process id into a file and then when a new instance starts, check the file to see if the old instance is still running.

    0 讨论(0)
  • 2021-02-08 01:18

    You can use the run-one package, which provides run-one, run-this-one and keep-one-running.

    The package: https://launchpad.net/ubuntu/+source/run-one

    The blog introducing it: http://blog.dustinkirkland.com/2011/02/introducing-run-one-and-run-this-one.html

    0 讨论(0)
  • 2021-02-08 01:28
    (
            if flock -n 9
            then
                    echo 'Not doing the critical operation (lock present).'
                    exit;
            fi
    
            # critical section goes here
    
    ) 9>'/run/lock/some_lock_file'
    rm -f '/run/lock/some_lock_file'
    

    From example in flock(1) man page. Very practical for using in shell scripts.

    0 讨论(0)
提交回复
热议问题