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

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

    The existing answers posted either rely on the CLI utility flock or do not properly secure the lock file. The flock utility is not available on all non-Linux systems (i.e. FreeBSD), and does not work properly on NFS.

    In my early days of system administration and system development, I was told that a safe and relatively portable method of creating a lock file was to create a temp file using mkemp(3) or mkemp(1), write identifying information to the temp file (i.e. PID), then hard link the temp file to the lock file. If the link was successful, then you have successfully obtained the lock.

    When using locks in shell scripts, I typically place an obtain_lock() function in a shared profile and then source it from the scripts. Below is an example of my lock function:

    obtain_lock()
    {
      LOCK="${1}"
      LOCKDIR="$(dirname "${LOCK}")"
      LOCKFILE="$(basename "${LOCK}")"
    
      # create temp lock file
      TMPLOCK=$(mktemp -p "${LOCKDIR}" "${LOCKFILE}XXXXXX" 2> /dev/null)
      if test "x${TMPLOCK}" == "x";then
         echo "unable to create temporary file with mktemp" 1>&2
         return 1
      fi
      echo "$$" > "${TMPLOCK}"
    
      # attempt to obtain lock file
      ln "${TMPLOCK}" "${LOCK}" 2> /dev/null
      if test $? -ne 0;then
         rm -f "${TMPLOCK}"
         echo "unable to obtain lockfile" 1>&2
         if test -f "${LOCK}";then
            echo "current lock information held by: $(cat "${LOCK}")" 1>&2
         fi
         return 2
      fi
      rm -f "${TMPLOCK}"
    
      return 0;
    };
    

    The following is an example of how to use the lock function:

    #!/bin/sh
    
    . /path/to/locking/profile.sh
    PROG_LOCKFILE="/tmp/myprog.lock"
    
    clean_up()
    {
      rm -f "${PROG_LOCKFILE}"
    }
    
    obtain_lock "${PROG_LOCKFILE}"
    if test $? -ne 0;then
       exit 1
    fi
    trap clean_up SIGHUP SIGINT SIGTERM
    
    # bulk of script
    
    clean_up
    exit 0
    # end of script
    

    Remember to call clean_up at any exit points in your script.

    I've used the above in both Linux and FreeBSD environments.

提交回复
热议问题