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

前端 未结 30 3026
忘掉有多难
忘掉有多难 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:21

    All approaches that test the existence of "lock files" are flawed.

    Why? Because there is no way to check whether a file exists and create it in a single atomic action. Because of this; there is a race condition that WILL make your attempts at mutual exclusion break.

    Instead, you need to use mkdir. mkdir creates a directory if it doesn't exist yet, and if it does, it sets an exit code. More importantly, it does all this in a single atomic action making it perfect for this scenario.

    if ! mkdir /tmp/myscript.lock 2>/dev/null; then
        echo "Myscript is already running." >&2
        exit 1
    fi
    

    For all details, see the excellent BashFAQ: http://mywiki.wooledge.org/BashFAQ/045

    If you want to take care of stale locks, fuser(1) comes in handy. The only downside here is that the operation takes about a second, so it isn't instant.

    Here's a function I wrote once that solves the problem using fuser:

    #       mutex file
    #
    # Open a mutual exclusion lock on the file, unless another process already owns one.
    #
    # If the file is already locked by another process, the operation fails.
    # This function defines a lock on a file as having a file descriptor open to the file.
    # This function uses FD 9 to open a lock on the file.  To release the lock, close FD 9:
    # exec 9>&-
    #
    mutex() {
        local file=$1 pid pids 
    
        exec 9>>"$file"
        { pids=$(fuser -f "$file"); } 2>&- 9>&- 
        for pid in $pids; do
            [[ $pid = $$ ]] && continue
    
            exec 9>&- 
            return 1 # Locked by a pid.
        done 
    }
    

    You can use it in a script like so:

    mutex /var/run/myscript.lock || { echo "Already running." >&2; exit 1; }
    

    If you don't care about portability (these solutions should work on pretty much any UNIX box), Linux' fuser(1) offers some additional options and there is also flock(1).

提交回复
热议问题