有什么快速而简单的方法可以确保在给定时间内只有一个shell脚本实例在运行?


当前回答

又快又脏?

#!/bin/sh

if [ -f sometempfile ]
  echo "Already running... will now terminate."
  exit
else
  touch sometempfile
fi

..do what you want here..

rm sometempfile

其他回答

PID和锁文件绝对是最可靠的。当您尝试运行程序时,它可以检查锁文件,如果它存在,它可以使用ps查看进程是否仍在运行。如果不是,脚本可以启动,将锁文件中的PID更新为自己的PID。

羊群走的路才是正确的。想想当脚本突然失效时会发生什么。在羊群的情况下,你只是松散的羊群,但这不是一个问题。另外,请注意,一个邪恶的伎俩是在脚本本身取一群。但这当然会让您完全陷入权限问题。

已经回答了一百万次了,但是另一种方式,不需要外部依赖:

LOCK_FILE="/var/lock/$(basename "$0").pid"
trap "rm -f ${LOCK_FILE}; exit" INT TERM EXIT
if [[ -f $LOCK_FILE && -d /proc/`cat $LOCK_FILE` ]]; then
   // Process already exists
   exit 1
fi
echo $$ > $LOCK_FILE

每次它将当前PID($$)写入锁文件,并在脚本启动时检查进程是否正在使用最新的PID运行。

您可以使用GNU Parallel,因为它在作为sem调用时是作为互斥量工作的。所以,具体来说,你可以使用:

sem --id SCRIPTSINGLETON yourScript

如果你也想要一个超时,使用:

sem --id SCRIPTSINGLETON --semaphoretimeout -10 yourScript

如果信号量在超时时间内没有释放,Timeout <0表示退出而不运行脚本,>的Timeout表示仍然运行脚本。

注意,您应该给它一个名称(使用——id),否则它默认为控制终端。

GNU Parallel在大多数Linux/OSX/Unix平台上是一个非常简单的安装程序——它只是一个Perl脚本。

现有的答案要么依赖于CLI实用程序群,要么没有正确地保护锁文件。flock实用程序在所有非linux系统(即FreeBSD)上都不可用,在NFS上也不能正常工作。

在我从事系统管理和系统开发的早期,有人告诉我,一种安全且相对可移植的创建锁文件的方法是使用mkemp(3)或mkemp(1)创建临时文件,将标识信息写入临时文件(即PID),然后将临时文件硬链接到锁文件。如果链接成功,那么您已经成功地获得了锁。

当在shell脚本中使用锁时,我通常会在共享概要文件中放置一个obtain_lock()函数,然后从脚本中获取它。下面是一个lock函数的例子:

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;
};

lock功能的使用示例如下:

#!/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

记住在脚本中的任何退出点调用clean_up。

我在Linux和FreeBSD环境中都使用了上述方法。