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

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

    Another option is to use shell's noclobber option by running set -C. Then > will fail if the file already exists.

    In brief:

    set -C
    lockfile="/tmp/locktest.lock"
    if echo "$$" > "$lockfile"; then
        echo "Successfully acquired lock"
        # do work
        rm "$lockfile"    # XXX or via trap - see below
    else
        echo "Cannot acquire lock - already locked by $(cat "$lockfile")"
    fi
    

    This causes the shell to call:

    open(pathname, O_CREAT|O_EXCL)
    

    which atomically creates the file or fails if the file already exists.


    According to a comment on BashFAQ 045, this may fail in ksh88, but it works in all my shells:

    $ strace -e trace=creat,open -f /bin/bash /home/mikel/bin/testopen 2>&1 | grep -F testopen.lock
    open("/tmp/testopen.lock", O_WRONLY|O_CREAT|O_EXCL|O_LARGEFILE, 0666) = 3
    
    $ strace -e trace=creat,open -f /bin/zsh /home/mikel/bin/testopen 2>&1 | grep -F testopen.lock
    open("/tmp/testopen.lock", O_WRONLY|O_CREAT|O_EXCL|O_NOCTTY|O_LARGEFILE, 0666) = 3
    
    $ strace -e trace=creat,open -f /bin/pdksh /home/mikel/bin/testopen 2>&1 | grep -F testopen.lock
    open("/tmp/testopen.lock", O_WRONLY|O_CREAT|O_EXCL|O_TRUNC|O_LARGEFILE, 0666) = 3
    
    $ strace -e trace=creat,open -f /bin/dash /home/mikel/bin/testopen 2>&1 | grep -F testopen.lock
    open("/tmp/testopen.lock", O_WRONLY|O_CREAT|O_EXCL|O_LARGEFILE, 0666) = 3
    

    Interesting that pdksh adds the O_TRUNC flag, but obviously it's redundant:
    either you're creating an empty file, or you're not doing anything.


    How you do the rm depends on how you want unclean exits to be handled.

    Delete on clean exit

    New runs fail until the issue that caused the unclean exit to be resolved and the lockfile is manually removed.

    # acquire lock
    # do work (code here may call exit, etc.)
    rm "$lockfile"
    

    Delete on any exit

    New runs succeed provided the script is not already running.

    trap 'rm "$lockfile"' EXIT
    

提交回复
热议问题