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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6482602/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 04:45:28  来源:igfitidea点击:

How to make sure only one instance of a Bash script is running at a time?

linuxbashconcurrencyscheduled-tasks

提问by NightWolf

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

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

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??

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

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.

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

采纳答案by cxreg

You want a pid file, maybe something like this:

你想要一个pid文件,可能是这样的:

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

回答by Noufal Ibrahim

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.

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

回答by alexandroid

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.

我认为您需要使用 lockfile 命令。请参阅在 shell 脚本 (BASH)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.

第二篇文章使用“手工制作的锁文件”,展示了如何捕捉脚本终止和释放锁;尽管lockfile -l <timeout seconds>在大多数情况下使用可能是一个足够好的选择。

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.lockis still there, that probably means previous instance of the script was killed before it removed script.lock. In that case we can tell lockfileto force re-create the lock after a timeout (X = 10 below):

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

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

Since lockfilecan 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).

由于lockfile可以创建多个锁定文件,因此有一个参数来指导它在重试获取它需要的下一个文件(-<sleep before retry, seconds>-r <number of retries>)之前应该等待多长时间。-s <suspend seconds>当锁定被强制移除时,还有一个等待时间参数(这是对强制打破锁定之前用于等待的超时的补充)。

回答by blueyed

You can use the run-onepackage, which provides run-one, run-this-oneand keep-one-running.

您可以使用run-one提供run-onerun-this-one和的包keep-one-running

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

包: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

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

回答by Josef Kufner

(
        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.

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

回答by Alexander Mills

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

我刚刚写了一个工具来做到这一点:https: //github.com/ORESoftware/quicklock

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

编写一个好的脚本大约需要 15 loc,所以不是你想在每个 shell 脚本中包含的东西。

basically works like this:

基本上是这样工作的:

$ ql_acquire_lock

the above calls this bash function:

上面调用了这个 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;
}

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

当脚本退出时,它会使用 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:

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

function ql_maybe_fail {
  if [[ "" == "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 "";
     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 ""
     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 ""; }
   trap - EXIT  # clear/unset trap

}