Quick-and-dirty way to ensure only one instance of a shell script is running at a time

前端 未结 30 2423
忘掉有多难
忘掉有多难 2020-11-22 02:57

What\'s a quick-and-dirty way to make sure that only one instance of a shell script is running at a given time?

30条回答
  •  孤独总比滥情好
    2020-11-22 03:27

    When targeting a Debian machine I find the lockfile-progs package to be a good solution. procmail also comes with a lockfile tool. However sometimes I am stuck with neither of these.

    Here's my solution which uses mkdir for atomic-ness and a PID file to detect stale locks. This code is currently in production on a Cygwin setup and works well.

    To use it simply call exclusive_lock_require when you need get exclusive access to something. An optional lock name parameter lets you share locks between different scripts. There's also two lower level functions (exclusive_lock_try and exclusive_lock_retry) should you need something more complex.

    function exclusive_lock_try() # [lockname]
    {
    
        local LOCK_NAME="${1:-`basename $0`}"
    
        LOCK_DIR="/tmp/.${LOCK_NAME}.lock"
        local LOCK_PID_FILE="${LOCK_DIR}/${LOCK_NAME}.pid"
    
        if [ -e "$LOCK_DIR" ]
        then
            local LOCK_PID="`cat "$LOCK_PID_FILE" 2> /dev/null`"
            if [ ! -z "$LOCK_PID" ] && kill -0 "$LOCK_PID" 2> /dev/null
            then
                # locked by non-dead process
                echo "\"$LOCK_NAME\" lock currently held by PID $LOCK_PID"
                return 1
            else
                # orphaned lock, take it over
                ( echo $$ > "$LOCK_PID_FILE" ) 2> /dev/null && local LOCK_PID="$$"
            fi
        fi
        if [ "`trap -p EXIT`" != "" ]
        then
            # already have an EXIT trap
            echo "Cannot get lock, already have an EXIT trap"
            return 1
        fi
        if [ "$LOCK_PID" != "$$" ] &&
            ! ( umask 077 && mkdir "$LOCK_DIR" && umask 177 && echo $$ > "$LOCK_PID_FILE" ) 2> /dev/null
        then
            local LOCK_PID="`cat "$LOCK_PID_FILE" 2> /dev/null`"
            # unable to acquire lock, new process got in first
            echo "\"$LOCK_NAME\" lock currently held by PID $LOCK_PID"
            return 1
        fi
        trap "/bin/rm -rf \"$LOCK_DIR\"; exit;" EXIT
    
        return 0 # got lock
    
    }
    
    function exclusive_lock_retry() # [lockname] [retries] [delay]
    {
    
        local LOCK_NAME="$1"
        local MAX_TRIES="${2:-5}"
        local DELAY="${3:-2}"
    
        local TRIES=0
        local LOCK_RETVAL
    
        while [ "$TRIES" -lt "$MAX_TRIES" ]
        do
    
            if [ "$TRIES" -gt 0 ]
            then
                sleep "$DELAY"
            fi
            local TRIES=$(( $TRIES + 1 ))
    
            if [ "$TRIES" -lt "$MAX_TRIES" ]
            then
                exclusive_lock_try "$LOCK_NAME" > /dev/null
            else
                exclusive_lock_try "$LOCK_NAME"
            fi
            LOCK_RETVAL="${PIPESTATUS[0]}"
    
            if [ "$LOCK_RETVAL" -eq 0 ]
            then
                return 0
            fi
    
        done
    
        return "$LOCK_RETVAL"
    
    }
    
    function exclusive_lock_require() # [lockname] [retries] [delay]
    {
        if ! exclusive_lock_retry "$@"
        then
            exit 1
        fi
    }
    

提交回复
热议问题