确保Bash脚本只有一个实例正在运行的最佳方法是什么?(复制)

时间:2022-01-12 16:23:35

This question already has an answer here:

这个问题已经有了答案:

What is the simplest/best way to ensure only one instance of a given script is running - assuming it's Bash on Linux?

确保给定脚本只有一个实例正在运行的最简单/最好的方法是什么?

At the moment I'm doing:

此刻我正在做:

ps -C script.name.sh > /dev/null 2>&1 || ./script.name.sh

but it has several issues:

但它有几个问题:

  1. it puts the check outside of script
  2. 它将检查放在脚本之外
  3. it doesn't let me run the same script from separate accounts - which I would like sometimes.
  4. 它不允许我从不同的帐户运行相同的脚本——我有时希望这样。
  5. -C checks only first 14 characters of process name
  6. -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处理,但是我感觉应该有一个简单的方法来完成它。

14 个解决方案

#1


89  

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 /。锁已经被获取,您的脚本将是唯一可以访问执行的。完成后,只需删除锁。在脚本形式中,这可能看起来像:

#!/bin/bash

lockfile -r 0 /tmp/the.lock || exit 1

# Do stuff here

rm -f /tmp/the.lock

#2


102  

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(来自util-linux[-ng])而不是lockfile(来自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.

为了您的方便,这是上面提到的样板。

#!/bin/bash
# SPDX-License-Identifier: MIT

## Copyright (C) 2009 Przemyslaw Pawelczyk <przemoc@gmail.com>
##
## This script is licensed under the terms of the MIT license.
## https://opensource.org/licenses/MIT
#
# Lockable script boilerplate

### HEADER ###

LOCKFILE="/var/lock/`basename $0`"
LOCKFD=99

# PRIVATE
_lock()             { flock -$1 $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.

#3


21  

I think flock is probably the easiest (and most memorable) variant. I use it in a cron job to auto-encode dvds and cds

我认为flock可能是最容易(也是最令人难忘)的变体。我在cron工作中使用它自动编码dvd和cd

# try to run a command, but fail immediately if it's already running
flock -n /var/lock/myjob.lock   my_bash_command

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用于超时或排除选项,直到锁被释放。最后,man页面显示了多个命令的一个很好的示例:

   (
     flock -n 9 || exit 1
     # ... commands executed under lock ...
   ) 9>/var/lock/mylockfile

#4


4  

Use the set -o noclobber option and attempt to overwrite a common file.

A short example

一个简短的例子

if ! (set -o noclobber ; echo > /tmp/global.lock) ; then
    exit 1  # the global.lock already exists
fi
# ...remainder of script...

A longer example. This example will wait for the global.lock but timeout after too long.

一个长时间的例子。这个示例将等待全局。锁定时间过长,但超时。

 function lockfile_waithold()
 {
    declare -ir time_beg=$(date '+%s')
    declare -ir maxtime=7140  # 7140 s = 1 hour 59 min.

    # waiting up to ${maxtime}s for /tmp/global.lock ...
    while ! \
       (set -o noclobber ; \
        echo -e "DATE:$(date)\nUSER:$(whoami)\nPID:$$" > /tmp/global.lock \ 
       ) 2>/dev/null
    do
        if [ $(( $(date '+%s') - ${time_beg})) -gt ${maxtime} ] ; then
            echo "waited too long for /tmp/global.lock" 1>&2
            return 1
        fi
        sleep 1
    done

    return 0
 }

 function lockfile_release()
 {
    rm -f /tmp/global.lock
 }

 if ! lockfile_waithold ; then
      exit 1
 fi

 # ...remainder of script

 lockfile_release

Repost from here by @Barry Kelly .

@Barry Kelly发回的报道。

#5


3  

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

话虽如此,您可以考虑将流程控制与您的脚本分离开来——使用一个start脚本。或者,至少把它分解成保存在单独文件中的函数,这样您在调用者脚本中可以有:

. 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

in each script that needs the control logic. The trap ensures that the lockfile gets removed when the caller exits, so you don't have to code this on each exit point in the script.

在需要控制逻辑的每个脚本中。该陷阱确保在调用者退出时删除lockfile,因此您不必在脚本的每个退出点上对其进行编码。

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.

使用单独的控制脚本意味着您可以对边缘情况进行全面检查:删除陈旧的日志文件,验证lockfile是否正确地与当前运行的脚本实例相关联,提供一个选项来终止正在运行的进程,等等。这也意味着您有更好的机会在ps输出中使用grep。可以使用ps-grep来验证一个lockfile是否有一个与之关联的正在运行的进程。也许您可以以某种方式命名您的lockfiles,以包含关于流程的信息:用户、pid等,稍后的脚本调用可以使用它来决定创建lockfile的进程是否仍然存在。

#6


2  

first test example

[[ $(lsof -t $0| wc -l) > 1 ]] && echo "At least one of $0 is running"

second test example

currsh=$0
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

explanation

"lsof -t" to list all pids of current running scripts named "$0".

“lsof -t”列出所有当前运行脚本的“$0”。

Command "lsof" will do two advantages.

命令“lsof”有两个优点。

  1. Ignore pids which is editing by editor such as vim, because vim edit its mapping file such as ".file.swp".
  2. 忽略由编辑器(如vim)编辑的pid,因为vim编辑它的映射文件(如“.file.swp”)。
  3. 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.
  4. 忽略由当前运行的shell脚本分叉的pid,这是大多数“grep”派生命令无法实现的。使用“pstree -pH pidnum”命令查看当前进程分叉状态的详细信息。

#7


1  

Ubuntu/Debian distros have the start-stop-daemon tool which is for the same purpose you describe. See also /etc/init.d/skeleton to see how it is used in writing start/stop scripts.

Ubuntu/Debian发行版有start-stop-daemon工具,目的与您描述的相同。参见/etc/init.要了解如何使用它来编写启动/停止脚本。

-- Noah

——诺亚

#8


1  

I'd also recommend looking at chpst (part of runit):

我也建议你看看chpst (runit的一部分):

chpst -L /tmp/your-lockfile.loc ./script.name.sh

#9


1  

One line ultimate solution:

一行的终极解决方案:

[ "$(pgrep -fn $0)" -ne "$(pgrep -fo $0)" ] && echo "At least 2 copies of $0 are running"

#10


1  

i found this in procmail package dependencies:

我在procmail包依赖项中发现了这一点:

apt install liblockfile-bin

apt安装liblockfile-bin

To run: dotlockfile -l file.lock

运行:dotlockfile -l file.lock

file.lock will be created.

文件。锁将被创建。

To unlock: dotlockfile -u file.lock

解锁:dotlockfile -u file.lock

Use this to list this package files / command: dpkg-query -L liblockfile-bin

使用它列出这个包文件/命令:dpkg -L liblockfile-bin

#11


0  

I had the same problem, and came up with a template that uses lockfile, a pid file that holds the process id number, and a kill -0 $(cat $pid_file) check to make aborted scripts not stop the next run. This creates a foobar-$USERID folder in /tmp where the lockfile and pid file lives.

我遇到了同样的问题,并提出了一个使用lockfile的模板,一个保存进程id号的pid文件,以及一个kill -0 $(cat $pid_file)检查,以使中止脚本不会停止下一次运行。这将在/tmp中创建一个foobar-$USERID文件夹,其中包含lockfile和pid文件。

You can still call the script and do other things, as long as you keep those actions in alertRunningPS.

您仍然可以调用脚本并执行其他操作,只要您将这些操作保存在alertRunningPS中。

#!/bin/bash

user_id_num=$(id -u)
pid_file="/tmp/foobar-$user_id_num/foobar-$user_id_num.pid"
lock_file="/tmp/foobar-$user_id_num/running.lock"
ps_id=$$

function alertRunningPS () {
    local PID=$(cat "$pid_file" 2> /dev/null)
    echo "Lockfile present. ps id file: $PID"
    echo "Checking if process is actually running or something left over from crash..."
    if kill -0 $PID 2> /dev/null; then
        echo "Already running, exiting"
        exit 1
    else
        echo "Not running, removing lock and continuing"
        rm -f "$lock_file"
        lockfile -r 0 "$lock_file"
    fi
}

echo "Hello, checking some stuff before locking stuff"

# Lock further operations to one process
mkdir -p /tmp/foobar-$user_id_num
lockfile -r 0 "$lock_file" || alertRunningPS

# Do stuff here
echo -n $ps_id > "$pid_file"
echo "Running stuff in ONE ps"

sleep 30s

rm -f "$lock_file"
rm -f "$pid_file"
exit 0

#12


-1  

from with your script:

从与你的脚本:

ps -ef | grep $0 | grep $(whoami)

#13


-1  

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:

在剧本的开头,我给出了:

pidof -s -o '%PPID' -x $( basename $0 ) > /dev/null 2>&1 && exit

Apparently pidof works great in a way that:

显然pidof的伟大之处在于:

  • it doesn't have limit on program name like ps -C ...
  • 它对程序名没有限制,比如ps -C…
  • it doesn't require me to do grep -v grep ( or anything similar )
  • 它不需要我做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:

至于检查“每个运行中的用户都有一个脚本的副本”,我写了这个,但是我不是很满意:

(
    pidof -s -o '%PPID' -x $( basename $0 ) | tr ' ' '\n'
    ps xo pid= | tr -cd '[0-9\n]'
) | sort | uniq -d

and then I check its output - if it's empty - there are no copies of the script from same user.

然后我检查它的输出——如果它是空的——没有来自同一用户的脚本副本。

#14


-2  

Here's our standard bit. It can recover from the script somehow dying without cleaning up it's lockfile.

这是我们的标准。它可以从脚本中恢复,而不需要清理它的锁文件。

It writes the process ID to the lock file if it runs normally. If it finds a lock file when it starts running, it will read the process ID from the lock file and check if that process exists. If the process does not exist it will remove the stale lock file and carry on. And only if the lock file exists AND the process is still running will it exit. And it writes a message when it exits.

如果进程正常运行,则将进程ID写入锁文件。如果它在开始运行时找到一个锁文件,它将从锁文件中读取进程ID并检查该进程是否存在。如果进程不存在,它将删除过期的锁文件并继续执行。只有当锁文件存在且进程仍在运行时,它才会退出。它在退出时写入一条消息。

# lock to ensure we don't get two copies of the same job
script_name="myscript.sh"
lock="/var/run/${script_name}.pid"
if [[ -e "${lock}" ]]; then
    pid=$(cat ${lock})
    if [[ -e /proc/${pid} ]]; then
        echo "${script_name}: Process ${pid} is still running, exiting."
        exit 1
    else
        # Clean up previous lock file
        rm -f ${lock}
   fi
fi
trap "rm -f ${lock}; exit $?" INT TERM EXIT
# write $$ (PID) to the lock file
echo "$$" > ${lock}

#1


89  

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 /。锁已经被获取,您的脚本将是唯一可以访问执行的。完成后,只需删除锁。在脚本形式中,这可能看起来像:

#!/bin/bash

lockfile -r 0 /tmp/the.lock || exit 1

# Do stuff here

rm -f /tmp/the.lock

#2


102  

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(来自util-linux[-ng])而不是lockfile(来自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.

为了您的方便,这是上面提到的样板。

#!/bin/bash
# SPDX-License-Identifier: MIT

## Copyright (C) 2009 Przemyslaw Pawelczyk <przemoc@gmail.com>
##
## This script is licensed under the terms of the MIT license.
## https://opensource.org/licenses/MIT
#
# Lockable script boilerplate

### HEADER ###

LOCKFILE="/var/lock/`basename $0`"
LOCKFD=99

# PRIVATE
_lock()             { flock -$1 $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.

#3


21  

I think flock is probably the easiest (and most memorable) variant. I use it in a cron job to auto-encode dvds and cds

我认为flock可能是最容易(也是最令人难忘)的变体。我在cron工作中使用它自动编码dvd和cd

# try to run a command, but fail immediately if it's already running
flock -n /var/lock/myjob.lock   my_bash_command

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用于超时或排除选项,直到锁被释放。最后,man页面显示了多个命令的一个很好的示例:

   (
     flock -n 9 || exit 1
     # ... commands executed under lock ...
   ) 9>/var/lock/mylockfile

#4


4  

Use the set -o noclobber option and attempt to overwrite a common file.

A short example

一个简短的例子

if ! (set -o noclobber ; echo > /tmp/global.lock) ; then
    exit 1  # the global.lock already exists
fi
# ...remainder of script...

A longer example. This example will wait for the global.lock but timeout after too long.

一个长时间的例子。这个示例将等待全局。锁定时间过长,但超时。

 function lockfile_waithold()
 {
    declare -ir time_beg=$(date '+%s')
    declare -ir maxtime=7140  # 7140 s = 1 hour 59 min.

    # waiting up to ${maxtime}s for /tmp/global.lock ...
    while ! \
       (set -o noclobber ; \
        echo -e "DATE:$(date)\nUSER:$(whoami)\nPID:$$" > /tmp/global.lock \ 
       ) 2>/dev/null
    do
        if [ $(( $(date '+%s') - ${time_beg})) -gt ${maxtime} ] ; then
            echo "waited too long for /tmp/global.lock" 1>&2
            return 1
        fi
        sleep 1
    done

    return 0
 }

 function lockfile_release()
 {
    rm -f /tmp/global.lock
 }

 if ! lockfile_waithold ; then
      exit 1
 fi

 # ...remainder of script

 lockfile_release

Repost from here by @Barry Kelly .

@Barry Kelly发回的报道。

#5


3  

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

话虽如此,您可以考虑将流程控制与您的脚本分离开来——使用一个start脚本。或者,至少把它分解成保存在单独文件中的函数,这样您在调用者脚本中可以有:

. 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

in each script that needs the control logic. The trap ensures that the lockfile gets removed when the caller exits, so you don't have to code this on each exit point in the script.

在需要控制逻辑的每个脚本中。该陷阱确保在调用者退出时删除lockfile,因此您不必在脚本的每个退出点上对其进行编码。

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.

使用单独的控制脚本意味着您可以对边缘情况进行全面检查:删除陈旧的日志文件,验证lockfile是否正确地与当前运行的脚本实例相关联,提供一个选项来终止正在运行的进程,等等。这也意味着您有更好的机会在ps输出中使用grep。可以使用ps-grep来验证一个lockfile是否有一个与之关联的正在运行的进程。也许您可以以某种方式命名您的lockfiles,以包含关于流程的信息:用户、pid等,稍后的脚本调用可以使用它来决定创建lockfile的进程是否仍然存在。

#6


2  

first test example

[[ $(lsof -t $0| wc -l) > 1 ]] && echo "At least one of $0 is running"

second test example

currsh=$0
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

explanation

"lsof -t" to list all pids of current running scripts named "$0".

“lsof -t”列出所有当前运行脚本的“$0”。

Command "lsof" will do two advantages.

命令“lsof”有两个优点。

  1. Ignore pids which is editing by editor such as vim, because vim edit its mapping file such as ".file.swp".
  2. 忽略由编辑器(如vim)编辑的pid,因为vim编辑它的映射文件(如“.file.swp”)。
  3. 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.
  4. 忽略由当前运行的shell脚本分叉的pid,这是大多数“grep”派生命令无法实现的。使用“pstree -pH pidnum”命令查看当前进程分叉状态的详细信息。

#7


1  

Ubuntu/Debian distros have the start-stop-daemon tool which is for the same purpose you describe. See also /etc/init.d/skeleton to see how it is used in writing start/stop scripts.

Ubuntu/Debian发行版有start-stop-daemon工具,目的与您描述的相同。参见/etc/init.要了解如何使用它来编写启动/停止脚本。

-- Noah

——诺亚

#8


1  

I'd also recommend looking at chpst (part of runit):

我也建议你看看chpst (runit的一部分):

chpst -L /tmp/your-lockfile.loc ./script.name.sh

#9


1  

One line ultimate solution:

一行的终极解决方案:

[ "$(pgrep -fn $0)" -ne "$(pgrep -fo $0)" ] && echo "At least 2 copies of $0 are running"

#10


1  

i found this in procmail package dependencies:

我在procmail包依赖项中发现了这一点:

apt install liblockfile-bin

apt安装liblockfile-bin

To run: dotlockfile -l file.lock

运行:dotlockfile -l file.lock

file.lock will be created.

文件。锁将被创建。

To unlock: dotlockfile -u file.lock

解锁:dotlockfile -u file.lock

Use this to list this package files / command: dpkg-query -L liblockfile-bin

使用它列出这个包文件/命令:dpkg -L liblockfile-bin

#11


0  

I had the same problem, and came up with a template that uses lockfile, a pid file that holds the process id number, and a kill -0 $(cat $pid_file) check to make aborted scripts not stop the next run. This creates a foobar-$USERID folder in /tmp where the lockfile and pid file lives.

我遇到了同样的问题,并提出了一个使用lockfile的模板,一个保存进程id号的pid文件,以及一个kill -0 $(cat $pid_file)检查,以使中止脚本不会停止下一次运行。这将在/tmp中创建一个foobar-$USERID文件夹,其中包含lockfile和pid文件。

You can still call the script and do other things, as long as you keep those actions in alertRunningPS.

您仍然可以调用脚本并执行其他操作,只要您将这些操作保存在alertRunningPS中。

#!/bin/bash

user_id_num=$(id -u)
pid_file="/tmp/foobar-$user_id_num/foobar-$user_id_num.pid"
lock_file="/tmp/foobar-$user_id_num/running.lock"
ps_id=$$

function alertRunningPS () {
    local PID=$(cat "$pid_file" 2> /dev/null)
    echo "Lockfile present. ps id file: $PID"
    echo "Checking if process is actually running or something left over from crash..."
    if kill -0 $PID 2> /dev/null; then
        echo "Already running, exiting"
        exit 1
    else
        echo "Not running, removing lock and continuing"
        rm -f "$lock_file"
        lockfile -r 0 "$lock_file"
    fi
}

echo "Hello, checking some stuff before locking stuff"

# Lock further operations to one process
mkdir -p /tmp/foobar-$user_id_num
lockfile -r 0 "$lock_file" || alertRunningPS

# Do stuff here
echo -n $ps_id > "$pid_file"
echo "Running stuff in ONE ps"

sleep 30s

rm -f "$lock_file"
rm -f "$pid_file"
exit 0

#12


-1  

from with your script:

从与你的脚本:

ps -ef | grep $0 | grep $(whoami)

#13


-1  

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:

在剧本的开头,我给出了:

pidof -s -o '%PPID' -x $( basename $0 ) > /dev/null 2>&1 && exit

Apparently pidof works great in a way that:

显然pidof的伟大之处在于:

  • it doesn't have limit on program name like ps -C ...
  • 它对程序名没有限制,比如ps -C…
  • it doesn't require me to do grep -v grep ( or anything similar )
  • 它不需要我做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:

至于检查“每个运行中的用户都有一个脚本的副本”,我写了这个,但是我不是很满意:

(
    pidof -s -o '%PPID' -x $( basename $0 ) | tr ' ' '\n'
    ps xo pid= | tr -cd '[0-9\n]'
) | sort | uniq -d

and then I check its output - if it's empty - there are no copies of the script from same user.

然后我检查它的输出——如果它是空的——没有来自同一用户的脚本副本。

#14


-2  

Here's our standard bit. It can recover from the script somehow dying without cleaning up it's lockfile.

这是我们的标准。它可以从脚本中恢复,而不需要清理它的锁文件。

It writes the process ID to the lock file if it runs normally. If it finds a lock file when it starts running, it will read the process ID from the lock file and check if that process exists. If the process does not exist it will remove the stale lock file and carry on. And only if the lock file exists AND the process is still running will it exit. And it writes a message when it exits.

如果进程正常运行,则将进程ID写入锁文件。如果它在开始运行时找到一个锁文件,它将从锁文件中读取进程ID并检查该进程是否存在。如果进程不存在,它将删除过期的锁文件并继续执行。只有当锁文件存在且进程仍在运行时,它才会退出。它在退出时写入一条消息。

# lock to ensure we don't get two copies of the same job
script_name="myscript.sh"
lock="/var/run/${script_name}.pid"
if [[ -e "${lock}" ]]; then
    pid=$(cat ${lock})
    if [[ -e /proc/${pid} ]]; then
        echo "${script_name}: Process ${pid} is still running, exiting."
        exit 1
    else
        # Clean up previous lock file
        rm -f ${lock}
   fi
fi
trap "rm -f ${lock}; exit $?" INT TERM EXIT
# write $$ (PID) to the lock file
echo "$$" > ${lock}