48

GNU bash,版本 1.14.7(1)

我有一个名为“ abc.sh”的脚本,我只能从abc.sh脚本中检查这个......在里面我写了以下语句

status=`ps -efww | grep -w "abc.sh" | grep -v grep | grep -v $$ | awk '{ print $2 }'`
if [ ! -z "$status" ]; then
        echo "[`date`] : abc.sh : Process is already running"
        exit 1;
fi

我知道这是错误的,因为每次它在“ps”中找到自己的进程而退出时如何解决?如何检查脚本是否已经在运行from that script

4

15 回答 15

61

检查已经执行的进程的一种更简单的方法是pidof命令。

if pidof -x "abc.sh" >/dev/null; then
    echo "Process already running"
fi

或者,让您的脚本在执行时创建一个 PID 文件。然后是检查 PID 文件是否存在以确定进程是否已在运行的简单练习。

#!/bin/bash
# abc.sh

mypidfile=/var/run/abc.sh.pid

# Could add check for existence of mypidfile here if interlock is
# needed in the shell script itself.

# Ensure PID file is removed on program exit.
trap "rm -f -- '$mypidfile'" EXIT

# Create a file with current PID to indicate that process is running.
echo $$ > "$mypidfile"

...

更新: 问题现在已更改为从脚本本身进行检查。在这种情况下,我们希望总是看到至少有一个abc.sh运行。如果有多个abc.sh,那么我们知道该进程仍在运行。pidof如果进程已经在运行,我仍然建议使用将返回 2 个 PID 的命令。您可以使用grep过滤掉当前的 PID,在 shell 中循环,甚至恢复为仅计算 PIDwc以检测多个进程。

这是一个例子:

#!/bin/bash

for pid in $(pidof -x abc.sh); do
    if [ $pid != $$ ]; then
        echo "[$(date)] : abc.sh : Process is already running with PID $pid"
        exit 1
    fi
done
于 2013-05-29T07:32:57.970 回答
32

我想要“pidof”方法,这是诀窍:

    if pidof -o %PPID -x "abc.sh">/dev/null; then
        echo "Process already running"
    fi

参数-o %PPID告诉省略调用 shell 或 shell 脚本的 pid。pidof手册页中的更多信息。

于 2015-02-17T14:17:15.270 回答
11

这是您将在不同地方看到的一个技巧:

status=`ps -efww | grep -w "[a]bc.sh" | awk -vpid=$$ '$2 != pid { print $2 }'`
if [ ! -z "$status" ]; then
    echo "[`date`] : abc.sh : Process is already running"
    exit 1;
fi

[a](或选择不同的字母)周围的括号防止grep找到它自己。这使得该grep -v grep位不必要。我还移除grep -v $$并修复了该awk部分以完成同样的事情。

于 2013-05-29T21:42:01.223 回答
10

如果我在这里错了,请有人将我击落

我知道mkdir操作是原子的,所以你可以创建一个锁定目录

#!/bin/sh
lockdir=/tmp/AXgqg0lsoeykp9L9NZjIuaqvu7ANILL4foeqzpJcTs3YkwtiJ0
mkdir $lockdir  || {
    echo "lock directory exists. exiting"
    exit 1
}
# take pains to remove lock directory when script terminates
trap "rmdir $lockdir" EXIT INT KILL TERM

# rest of script here
于 2013-05-29T20:40:34.070 回答
9

这是我在 bash 脚本中的操作方式:

if ps ax | grep $0 | grep -v $$ | grep bash | grep -v grep
then
    echo "The script is already running."
    exit 1
fi

这使我可以将此代码段用于任何 bash 脚本。我需要 grep bash,因为与 cron 一起使用时,它会创建另一个使用 /bin/sh 执行它的进程。

于 2017-08-01T05:31:19.033 回答
7

我发现@Austin Phillips 的答案是正确的。我要做的一个小改进是添加 -o (忽略脚本本身的 pid)并匹配具有 basename 的脚本(即可以将相同的代码放入任何脚本中):

if pidof -x "`basename $0`" -o $$ >/dev/null; then
    echo "Process already running"
fi
于 2018-08-15T07:07:05.987 回答
5

工作解决方案:

if [[ `pgrep -f $0` != "$$" ]]; then
        echo "Another instance of shell already exist! Exiting"
        exit
fi

编辑:我最近查看了一些评论,所以我尝试通过一些调试来尝试相同的操作。我也会解释的。

解释:

  • $0 给出运行脚本的文件名。
  • $$ 给出运行脚本的 PID。
  • pgrep 按名称搜索进程并返回 PID。
  • pgrep -f $0 按文件名搜索,$0 是当前 bash 脚本文件名并返回其 PID。

因此,pgrep 检查您的脚本 PID ($0) 是否等于当前正在运行的脚本 ($$)。如果是,则脚本正常运行。如果没有,这意味着有另一个具有相同文件名的 PID 正在运行,所以它退出。我使用pgrep -f $0not 的原因pgrep bash是您可以运行多个 bash 实例,从而返回多个 PID。按文件名,它只返回单个 PID。

例外:

  1. bash script.sh不要使用./script.sh,因为除非你有shebang,否则它不起作用。
    • 修复:#!/bin/bash在开始时使用 shebang。
  2. sudo 不起作用的原因是它返回 pgrep 返回 bash 和 sudo 的 PID,而不是返回 bash。
    • 使固定:
#!/bin/bash
pseudopid="`pgrep -f $0 -l`"
actualpid="$(echo "$pseudopid" | grep -v 'sudo' | awk -F ' ' '{print $1}')"

if [[ `echo $actualpid` != "$$" ]]; then
    echo "Another instance of shell already exist! Exiting"
    exit
fi

while true
do
    echo "Running"
    sleep 100
done
  1. 即使脚本没有运行,脚本也会退出。那是因为有另一个进程具有相同的文件名。尝试执行vim script.sh然后运行bash script.sh,它会因为 vim 以相同的文件名打开而失败
    • 修复:使用唯一的文件名。
于 2020-07-01T12:49:37.053 回答
4

以稍微不同的方式使用 PS 命令也可以忽略子进程:

ps -eaf | grep -v grep | grep $PROCESS | grep -v $$
于 2016-01-20T13:37:55.187 回答
4

pidof不适合我,所以我搜索了更多并遇到了pgrep

for pid in $(pgrep -f my_script.sh); do
    if [ $pid != $$ ]; then
        echo "[$(date)] : my_script.sh : Process is already running with PID $pid"
        exit 1
    else
      echo "Running with PID $pid"
    fi  
done

部分取自上述答案和https://askubuntu.com/a/803106/802276

于 2018-04-26T02:00:53.693 回答
2

我不想abc.sh在支票中硬编码,所以我使用了以下内容:

MY_SCRIPT_NAME=`basename "$0"`
if pidof -o %PPID -x $MY_SCRIPT_NAME > /dev/null; then
    echo "$MY_SCRIPT_NAME already running; exiting"
    exit 1
fi
于 2018-05-12T14:31:22.353 回答
2

我发现使用反引号将命令输出捕获到一个变量中,不利地,会产生一个太多的ps aux结果,例如对于abc.sh的单个运行实例:

ps aux | grep -w "abc.sh" | grep -v grep | wc -l

返回“1”。然而,

count=`ps aux | grep -w "abc.sh" | grep -v grep | wc -l`
echo $count

返回“2”

似乎使用反引号构造以某种方式暂时创建了另一个过程。可能是 topicstarter 无法完成这项工作的原因。只需要减少 $count var。

于 2018-03-16T22:35:27.353 回答
2

我在执行期间创建了一个临时文件。

我就是这样做的:

#!/bin/sh
# check if lock file exists
if [ -e /tmp/script.lock ]; then
  echo "script is already running"
else
# create a lock file
  touch /tmp/script.lock
  echo "run script..."
#remove lock file
 rm /tmp/script.lock
fi
于 2017-01-20T18:07:00.757 回答
1

这是紧凑和通用的

# exit if another instance of this script is running
for pid in $(pidof -x `basename $0`); do
   [ $pid != $$ ] && { exit 1; }
done
于 2019-09-02T10:44:07.870 回答
1

最干净最快的方法:

processAlreadyRunning () {
    process="$(basename "${0}")"
    pidof -x "${process}" -o $$ &>/dev/null
}
于 2020-07-03T23:27:44.773 回答
-1

[ "$(pidof -x $(basename $0))" != $$ ] && exit

https://github.com/x-zhao/exit-if-bash-script-already-running/blob/master/script.sh

于 2019-10-22T19:03:07.750 回答