如何确保一次只有一个 Bash 脚本实例在运行?

发布于 2024-11-17 02:41:20 字数 231 浏览 8 评论 0原文

我想制作一个在任何时候最多只运行一次的 sh 脚本。

比如说,如果我执行脚本,然后我再次执行脚本,我该如何做到这一点,以便如果脚本的第一个执行仍在工作,第二个执行将失败并出现错误。即我需要在执行任何操作之前检查脚本是否在其他地方运行。我该怎么做呢?

我的脚本运行一个长时间运行的进程(即永远运行)。我想使用 cron 之类的东西每 15 分钟调用一次脚本,这样万一进程失败,它将由下一个 cron 运行脚本重新启动。

I want to make a sh script that will only run at most once at any point.

Say, if I exec the script then I go to exec the script again, how do I make it so that if the first exec of the script is still working the second one will fail with an error. I.e. I need to check if the script is running elsewhere before doing anything. How would I go about doing this??

The script I have runs a long running process (i.e. runs forever). I wanted to use something like cron to call the script every 15mins so in case the process fails, it will be restarted by the next cron run script.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

鹿港小镇 2024-11-24 02:41:20

你想要一个 pid 文件,也许是这样的:

pidfile=/path/to/pidfile
if [ -f "$pidfile" ] && kill -0 `cat $pidfile` 2>/dev/null; then
    echo still running
    exit 1
fi  
echo $ > $pidfile

You want a pid file, maybe something like this:

pidfile=/path/to/pidfile
if [ -f "$pidfile" ] && kill -0 `cat $pidfile` 2>/dev/null; then
    echo still running
    exit 1
fi  
echo $ > $pidfile
对不⑦ 2024-11-24 02:41:20

我认为你需要使用lockfile命令。请参阅在 shell 脚本 (BASH) 中使用锁定文件http://www.davidpashley.com/articles/writing-robust-shell-脚本.html

第二篇文章使用“手工制作的锁定文件”并展示如何捕获脚本终止和终止。释放锁;尽管在大多数情况下使用lockfile -l <​​timeout秒>可能是一个足够好的替代方案。

无超时的使用示例:

lockfile script.lock
<do some stuff>
rm -f script.lock

将确保在此期间启动的任何第二个脚本将无限期地等待文件被删除,然后再继续。

如果我们知道脚本运行时间不应超过 X 秒,并且 script.lock 仍然存在,这可能意味着脚本的前一个实例在删除 script.lock 之前已被终止/代码>。在这种情况下,我们可以告诉 lockfile 在超时后强制重新创建锁(下面的 X = 10):

lockfile -l 10 /tmp/mylockfile
<do some stuff>
rm -f /tmp/mylockfile

由于 lockfile 可以创建多个锁文件,因此有一个参数指导它在重试获取所需的下一个文件之前应该等待多长时间(-<重试前睡眠,秒数>-r <重试次数> )。还有一个参数 -s <挂起秒数> 用于强制解除锁定时的等待时间(这补充了强制破坏锁定之前用于等待的超时时间)。

I think you need to use lockfile command. See using lockfiles in shell scripts (BASH) or http://www.davidpashley.com/articles/writing-robust-shell-scripts.html.

The second article uses "hand-made lock file" and shows how to catch script termination & releasing the lock; although using lockfile -l <timeout seconds> will probably be a good enough alternative for most cases.

Example of usage without timeout:

lockfile script.lock
<do some stuff>
rm -f script.lock

Will ensure that any second script started during this one will wait indefinitely for the file to be removed before proceeding.

If we know that the script should not run more than X seconds, and the script.lock is still there, that probably means previous instance of the script was killed before it removed script.lock. In that case we can tell lockfile to force re-create the lock after a timeout (X = 10 below):

lockfile -l 10 /tmp/mylockfile
<do some stuff>
rm -f /tmp/mylockfile

Since lockfile can create multiple lock files, there is a parameter to guide it how long it should wait before retrying to acquire the next file it needs (-<sleep before retry, seconds> and -r <number of retries>). There is also a parameter -s <suspend seconds> for wait time when the lock has been removed by force (which kind of complements the timeout used to wait before force-breaking the lock).

亣腦蒛氧 2024-11-24 02:41:20

您可以使用 run-one 包,它提供 run-onerun-this-onekeep-one-running

软件包:https://launchpad.net/ubuntu/+source/run-one

介绍它的博客:http://blog.dustinkirkland.com/2011/02/introducing-run-one-and-run-this-one.html

You can use the run-one package, which provides run-one, run-this-one and keep-one-running.

The package: https://launchpad.net/ubuntu/+source/run-one

The blog introducing it: http://blog.dustinkirkland.com/2011/02/introducing-run-one-and-run-this-one.html

数理化全能战士 2024-11-24 02:41:20

将进程 ID 写入文件,然后当新实例启动时,检查该文件以查看旧实例是否仍在运行。

Write the process id into a file and then when a new instance starts, check the file to see if the old instance is still running.

浮云落日 2024-11-24 02:41:20
(
        if flock -n 9
        then
                echo 'Not doing the critical operation (lock present).'
                exit;
        fi

        # critical section goes here

) 9>'/run/lock/some_lock_file'
rm -f '/run/lock/some_lock_file'

来自集群(1)手册页中的示例。在 shell 脚本中使用非常实用。

(
        if flock -n 9
        then
                echo 'Not doing the critical operation (lock present).'
                exit;
        fi

        # critical section goes here

) 9>'/run/lock/some_lock_file'
rm -f '/run/lock/some_lock_file'

From example in flock(1) man page. Very practical for using in shell scripts.

云归处 2024-11-24 02:41:20

我刚刚写了一个工具来执行此操作:
https://github.com/ORESoftware/quicklock

写一篇好的文章大约需要 15 loc,所以不是您想要包含在每个 shell 脚本中的内容。

基本上是这样工作的:

$ ql_acquire_lock

上面调用了这个bash函数:

function ql_acquire_lock {
  set -e;
  name="${1:-$PWD}"  # the lock name is the first argument, if that is empty, then set the lockname to $PWD
  mkdir -p "$HOME/.quicklock/locks"
  fle=$(echo "${name}" | tr "/" _)
  qln="$HOME/.quicklock/locks/${fle}.lock"
  mkdir "${qln}" &> /dev/null || { echo "${ql_magenta}quicklock: could not acquire lock with name '${qln}'${ql_no_color}."; exit 1; }
  export quicklock_name="${qln}";  # export the var *only if* above mkdir command succeeds
  trap on_ql_trap EXIT;
}

当脚本退出时,它会使用trap自动释放锁

function on_ql_trap {
   echo "quicklock: process with pid $ was trapped.";
   ql_release_lock
}

,随意手动释放锁,使用ql_release_lock

function ql_maybe_fail {
  if [[ "$1" == "true" ]]; then
      echo -e "${ql_magenta}quicklock: exiting with 1 since fail flag was set for your 'ql_release_lock' command.${ql_no_color}"
      exit 1;
  fi
}

function ql_release_lock () {

   if [[ -z "${quicklock_name}" ]]; then
     echo -e "quicklock: no lockname was defined. (\$quicklock_name was not set).";
     ql_maybe_fail "$1";
     return 0;
   fi

   if [[ "$HOME" == "${quicklock_name}" ]]; then
     echo -e "quicklock: dangerous value set for \$quicklock_name variable..was equal to user home directory, not good.";
     ql_maybe_fail "$1"
     return 0;
   fi

   rm -r "${quicklock_name}" &> /dev/null &&
   { echo -e "quicklock: lock with name '${quicklock_name}' was released.";  } ||
   { echo -e "quicklock: no lock existed for lockname '${quicklock_name}'."; ql_maybe_fail "$1"; }
   trap - EXIT  # clear/unset trap

}

I just wrote a tool that does this:
https://github.com/ORESoftware/quicklock

writing a good one takes about 15 loc, so not something you want to include in every shell script.

basically works like this:

$ ql_acquire_lock

the above calls this bash function:

function ql_acquire_lock {
  set -e;
  name="${1:-$PWD}"  # the lock name is the first argument, if that is empty, then set the lockname to $PWD
  mkdir -p "$HOME/.quicklock/locks"
  fle=$(echo "${name}" | tr "/" _)
  qln="$HOME/.quicklock/locks/${fle}.lock"
  mkdir "${qln}" &> /dev/null || { echo "${ql_magenta}quicklock: could not acquire lock with name '${qln}'${ql_no_color}."; exit 1; }
  export quicklock_name="${qln}";  # export the var *only if* above mkdir command succeeds
  trap on_ql_trap EXIT;
}

when the script exits, it automatically releases the lock using trap

function on_ql_trap {
   echo "quicklock: process with pid $ was trapped.";
   ql_release_lock
}

to manually release the lock at will, use ql_release_lock:

function ql_maybe_fail {
  if [[ "$1" == "true" ]]; then
      echo -e "${ql_magenta}quicklock: exiting with 1 since fail flag was set for your 'ql_release_lock' command.${ql_no_color}"
      exit 1;
  fi
}

function ql_release_lock () {

   if [[ -z "${quicklock_name}" ]]; then
     echo -e "quicklock: no lockname was defined. (\$quicklock_name was not set).";
     ql_maybe_fail "$1";
     return 0;
   fi

   if [[ "$HOME" == "${quicklock_name}" ]]; then
     echo -e "quicklock: dangerous value set for \$quicklock_name variable..was equal to user home directory, not good.";
     ql_maybe_fail "$1"
     return 0;
   fi

   rm -r "${quicklock_name}" &> /dev/null &&
   { echo -e "quicklock: lock with name '${quicklock_name}' was released.";  } ||
   { echo -e "quicklock: no lock existed for lockname '${quicklock_name}'."; ql_maybe_fail "$1"; }
   trap - EXIT  # clear/unset trap

}
可爱咩 2024-11-24 02:41:20

我建议使用flock,但方式与@Josef Kufner 建议的方式不同。我认为这非常简单,默认情况下 flock 应该在大多数系统上可用:

flock -n lockfile myscript.sh

I suggest using flock, but in a different way than suggested by @Josef Kufner. I think this is quite easy and flock should be available on most systems by default:

flock -n lockfile myscript.sh
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文