Linux 确保只有一个 Bash 脚本实例正在运行的最佳方法是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1715137/
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
What is the best way to ensure only one instance of a Bash script is running?
提问by
What is the simplest/best way to ensure only one instance of a given script is running - assuming it's Bash on Linux?
确保只运行给定脚本的一个实例的最简单/最佳方法是什么 - 假设它是 Linux 上的 Bash?
At the moment I'm doing:
目前我正在做:
ps -C script.name.sh > /dev/null 2>&1 || ./script.name.sh
but it has several issues:
但它有几个问题:
- it puts the check outside of script
- it doesn't let me run the same script from separate accounts - which I would like sometimes.
-C
checks only first 14 characters of process name
- 它将检查放在脚本之外
- 它不允许我从不同的帐户运行相同的脚本 - 我有时会喜欢。
-C
仅检查进程名称的前 14 个字符
Of course, I can write my own pidfile handling, but I sense that there should be a simple way to do it.
当然,我可以编写自己的 pidfile 处理,但我觉得应该有一个简单的方法来做到这一点。
采纳答案by ezpz
If the script is the same across all users, you can use a lockfile
approach. If you acquire the lock, proceed else show a message and exit.
如果所有用户的脚本都相同,则可以使用一种lockfile
方法。如果您获得了锁,请继续,否则会显示一条消息并退出。
As an example:
举个例子:
[Terminal #1] $ lockfile -r 0 /tmp/the.lock
[Terminal #1] $
[Terminal #2] $ lockfile -r 0 /tmp/the.lock
[Terminal #2] lockfile: Sorry, giving up on "/tmp/the.lock"
[Terminal #1] $ rm -f /tmp/the.lock
[Terminal #1] $
[Terminal #2] $ lockfile -r 0 /tmp/the.lock
[Terminal #2] $
After /tmp/the.lock
has been acquired your script will be the only one with access to execution. When you are done, just remove the lock. In script form this might look like:
后/tmp/the.lock
已经获得你的脚本将是唯一一个能够访问执行。完成后,只需取下锁即可。在脚本形式中,这可能如下所示:
#!/bin/bash
lockfile -r 0 /tmp/the.lock || exit 1
# Do stuff here
rm -f /tmp/the.lock
回答by ennuikiller
from with your script:
来自您的脚本:
ps -ef | grep . my_script_control.ksh
# Function exits if cannot start due to lockfile or prior running instance.
my_start_me_up lockfile_name;
trap "rm -f $lockfile_name; exit" 0 2 3 15
| grep $(whoami)
回答by martin clayton
I'm not sure there's any one-line robust solution, so you might end up rolling your own.
我不确定是否有任何可靠的单线解决方案,因此您最终可能会推出自己的解决方案。
Lockfiles are imperfect, but less so than using 'ps | grep | grep -v' pipelines.
锁文件是不完美的,但不如使用 'ps | | | grep -v' 管道。
Having said that, you might consider keeping the process control separate from your script - have a start script. Or, at least factor it out to functions held in a separate file, so you might in the caller script have:
话虽如此,您可能会考虑将过程控制与您的脚本分开 - 拥有一个启动脚本。或者,至少将其分解为保存在单独文件中的函数,因此您可能在调用者脚本中具有:
pidof -s -o '%PPID' -x $( basename (
pidof -s -o '%PPID' -x $( basename #!/bin/bash
# SPDX-License-Identifier: MIT
## Copyright (C) 2009 Przemyslaw Pawelczyk <[email protected]>
##
## This script is licensed under the terms of the MIT license.
## https://opensource.org/licenses/MIT
#
# Lockable script boilerplate
### HEADER ###
LOCKFILE="/var/lock/`basename chpst -L /tmp/your-lockfile.loc ./script.name.sh
`"
LOCKFD=99
# PRIVATE
_lock() { flock - $LOCKFD; }
_no_more_locking() { _lock u; _lock xn && rm -f $LOCKFILE; }
_prepare_locking() { eval "exec $LOCKFD>\"$LOCKFILE\""; trap _no_more_locking EXIT; }
# ON START
_prepare_locking
# PUBLIC
exlock_now() { _lock xn; } # obtain an exclusive lock immediately or fail
exlock() { _lock x; } # obtain an exclusive lock
shlock() { _lock s; } # obtain a shared lock
unlock() { _lock u; } # drop a lock
### BEGIN OF SCRIPT ###
# Simplest example is avoiding running multiple instances of script.
exlock_now || exit 1
# Remember! Lock file is removed when one of the scripts exits and it is
# the only script holding the lock or lock is not acquired at all.
) | tr ' ' '\n'
ps xo pid= | tr -cd '[0-9\n]'
) | sort | uniq -d
) > /dev/null 2>&1 && exit
in each script that needs the control logic. The trapensures that the lockfile gets removed when the caller exits, so you don't have to code this on each exit point in the script.
在需要控制逻辑的每个脚本中。该陷阱确保当呼叫者退出,所以您不必到这个代码在脚本的每个出口点锁文件被删除。
Using a separate control script means that you can sanity check for edge cases:
remove stale log files, verify that the lockfile is associated correctly with
a currently running instance of the script, give an option to kill the running process, and so on.
It also means you've got a better chance of using grep on ps
output successfully.
A ps-grep can be used to verify that a lockfile has a running process associated with it.
Perhaps you could name your lockfiles in some way to include information about the process:
user, pid, etc., which can be used by a later script invocation to decide whether the process
that created the lockfile is still around.
使用单独的控制脚本意味着您可以对边缘情况进行健全性检查:删除陈旧的日志文件,验证锁定文件是否与当前正在运行的脚本实例正确关联,提供终止正在运行的进程的选项,等等。这也意味着您有更好的机会ps
成功地在输出上使用 grep 。ps-grep 可用于验证锁定文件是否具有与之关联的正在运行的进程。也许您可以以某种方式命名您的锁文件以包含有关进程的信息:用户、pid 等,稍后脚本调用可以使用这些信息来确定创建锁文件的进程是否仍然存在。
回答by martin clayton
I found a pretty simple way to handle "one copy of script per system". It doesn't allow me to run multiple copies of the script from many accounts though (on standard Linux that is).
我找到了一种非常简单的方法来处理“每个系统一个脚本副本”。它不允许我从多个帐户运行脚本的多个副本(在标准 Linux 上)。
Solution:
解决方案:
At the beginning of script, I gave:
在脚本的开头,我给出了:
# try to run a command, but fail immediately if it's already running
flock -n /var/lock/myjob.lock my_bash_command
Apparently pidofworks great in a way that:
显然pidof 的工作方式很好:
- it doesn't have limit on program name like
ps -C ...
- it doesn't require me to do
grep -v grep
( or anything similar )
- 它对程序名称没有限制,例如
ps -C ...
- 它不需要我做
grep -v grep
(或任何类似的事情)
And it doesn't rely on lockfiles, which for me is a big win, because relaying on them means you have to add handling of stale lockfiles - which is not really complicated, but if it can be avoided - why not?
而且它不依赖于锁文件,这对我来说是一个巨大的胜利,因为依靠它们意味着您必须添加对陈旧锁文件的处理——这并不复杂,但如果可以避免的话——为什么不呢?
As for checking with "one copy of script per running user", i wrote this, but I'm not overly happy with it:
至于检查“每个运行用户的脚本副本”,我写了这个,但我对它并不太满意:
(
flock -n 9 || exit 1
# ... commands executed under lock ...
) 9>/var/lock/mylockfile
and then I check its output - if it's empty - there are no copies of the script from same user.
然后我检查它的输出 - 如果它是空的 - 没有来自同一用户的脚本副本。
回答by przemoc
Advisory locking has been used for ages and it can be used in bash scripts. I prefer simple flock
(from util-linux[-ng]
) over lockfile
(from procmail
). And always remember about a trap on exit (sigspec == EXIT
or 0
, trapping specific signals is superfluous) in those scripts.
咨询锁定已经使用了很长时间,它可以在 bash 脚本中使用。我更喜欢简单的flock
(from util-linux[-ng]
) 而不是lockfile
(from procmail
)。并且始终记住这些脚本中的退出陷阱(sigspec ==EXIT
或0
,捕获特定信号是多余的)。
In 2009 I released my lockable script boilerplate (originally available at my wiki page, nowadays available as gist). Transforming that into one-instance-per-user is trivial. Using it you can also easily write scripts for other scenarios requiring some locking or synchronization.
2009 年,我发布了我的可锁定脚本样板(最初可在我的 wiki 页面上获得,现在可作为gist 获得)。将其转换为每个用户一个实例是微不足道的。使用它,您还可以轻松地为需要一些锁定或同步的其他场景编写脚本。
Here is the mentioned boilerplate for your convenience.
为了您的方便,这里是提到的样板。
[ "$(pgrep -fn [[ $(lsof -t currsh=##代码##
currpid=$$
runpid=$(lsof -t $currsh| paste -s -d " ")
if [[ $runpid == $currpid ]]
then
sleep 11111111111111111
else
echo -e "\nPID($runpid)($currpid) ::: At least one of \"$currsh\" is running !!!\n"
false
exit 1
fi
| wc -l) > 1 ]] && echo "At least one of ##代码## is running"
)" -ne "$(pgrep -fo ##代码##)" ] && echo "At least 2 copies of ##代码## are running"
回答by Noah Spurrier
Ubuntu/Debian distros have the start-stop-daemon
tool which is for the same purpose you describe. See also /etc/init.d/skeletonto see how it is used in writing start/stop scripts.
Ubuntu/Debian 发行版具有start-stop-daemon
与您描述的目的相同的工具。另请参阅/etc/init.d/skeleton以了解如何使用它来编写启动/停止脚本。
-- Noah
——诺亚
回答by Jake Biesinger
I think flock
is probably the easiest (and most memorable) variant. I use it in a cron job to auto-encode dvdsand cds
我认为flock
可能是最简单(也是最令人难忘)的变体。我在 cron 作业中使用它来自动编码dvds和cds
Use -w
for timeouts or leave out options to wait until the lock is released. Finally, the man page shows a nice example for multiple commands:
使用-w
了超时或离开了选择等待,直到锁被释放。最后,手册页显示了多个命令的一个很好的例子:
回答by Dm1
One line ultimate solution:
一线终极解决方案:
##代码##回答by yanyingwang
first test example
第一个测试示例
##代码##second test example
第二个测试示例
##代码##explanation
解释
"lsof -t" to list all pids of current running scripts named "$0".
“lsof -t”列出名为“$0”的当前正在运行的脚本的所有pid。
Command "lsof" will do two advantages.
命令“lsof”有两个优点。
- Ignore pids which is editing by editor such as vim, because vim edit its mapping file such as ".file.swp".
- Ignore pids forked by current running shell scripts, which most "grep" derivative command can't achieve it. Use "pstree -pH pidnum" command to see details about current process forking status.
- 忽略由 vim 等编辑器编辑的 pids,因为 vim 编辑其映射文件,例如“.file.swp”。
- 忽略当前运行的 shell 脚本派生的 pids,这是大多数“grep”派生命令无法实现的。使用“pstree -pH pidnum”命令查看有关当前进程分叉状态的详细信息。