检查进程是否在运行,如果已经在运行则退出

6

我有一个包含方法的shell脚本:

start(){
echo "Hello world"
}

stop(){
ps -ef|grep script.sh|grep -v grep|xargs kill
}

while [ "$1" != "" ]; do
case "$1" in
        start)
            start
            ;;
        stop)
            stop
            ;;
        *)
            echo $"Usage: $0 {start|stop}"
            exit 1
        esac
   shift
done

我将使用以下命令运行此脚本:./script.sh start来调用开始方法。现在,我想检查这个进程是否已经在运行中,如果已经在运行中,则退出。我尝试了一些在线解决方案,但都没有起作用。请有人帮忙。 我尝试过的解决方案是:
if [ -f /var/tmp/script.lock ]; then
  echo "Already running. Exiting."
  exit 1
else
  touch /var/tmp/script.lock
fi
<main script code>
#some code
rm /var/tmp/script.lock

另外一个是:
PID=$(ps -ef | grep script.sh|grep -v grep)

   if [ -z $PID ]; then
       echo "Process already running"
       exit   
fi

这些解决方案在进程刚开始时就无法起作用并退出。


那些解决方案是什么?顺便说一句,你已经在“停止”时找到了所有运行实例的脚本,难道你不能在“启动”时做类似的事情吗? - bipll
当进程尚未运行时,-z $PID将为真。 - Barmar
你的 ps | grep 命令将会找到正在运行测试的脚本,因为它们的名称是相同的。 - Barmar
1
锁定文件的解决方案应该是可行的,它有什么问题吗? - Barmar
flock 是你的好朋友。 - Charles Duffy
显示剩余6条评论
2个回答

3

.lock 文件解决方案应该可行。唯一的问题是,如果脚本因错误而退出,并且没有删除锁定文件,则会出现问题。改进方法是在文件中存储进程的 PID,并检查该 PID 是否仍然存在。

if [ -f /var/tmp/script.lock ] && kill -0 $(cat /var/tmp/script.lock); then
  echo "Already running. Exiting."
  exit 1
else
  echo $$ > /var/tmp/script.lock
fi
<main script code>
#some code
rm /var/tmp/script.lock

kill -0并没有向进程发送信号,它只是测试是否可以发送信号:PID存在且您有权限向其发送信号(除非您以root身份运行,否则这意味着该进程正在使用与您相同的userid运行)。

尽管如此,仍有可能出现误报情况,如果脚本崩溃,然后其PID被同一用户重复使用。但是PID重用应该需要很长时间,并且它被同一用户重复使用的几率应该很低。

还有一种可能性是两个副本的脚本同时启动,它们都会看到没有锁定文件。如果您需要防止这种情况发生,您应该使用lockfile命令在检查文件的代码周围实现互斥。


“echo $$ >/var/tmp/script.lock” 不是完全原子操作 -- 尽管很小,但存在一个窗口,此时文件存在但尚未有内容。 - Charles Duffy
1
你为什么不使用现有的设施,如systemd来处理这个问题,而是自己编写呢? - Barmar
当您使用 ./script.sh start 命令时,它会在后台运行程序,因此 script.sh 退出了吗? - Barmar
这段代码假设 script.sh 会一直运行,直到 start 完成。如果 script.sh 立即退出,则 script.lock 文件将消失。 - Barmar
这是 flock() / fcntl(F_{GET,SET}LK) 锁文件方法的优势之一 -- 如果将锁文件放在守护进程继承的 FD 上,锁会一直保持到守护进程退出。 - Charles Duffy
显示剩余5条评论

1

这是我的脚本,希望有用。

#!/bin/bash
export JAVA_HOME=/usr/java/jdk1.7.0_25

checkpid()
{
        echo $(ps -ef | grep "LiquidityWarning.jar" | grep -v grep | awk '{ print $2}')
}

start ()
{
    if [ $(checkpid) ] ; then
        echo -e "\n$(date +%Y%m%d-%H:%M:%S) LiquidityWarning.jar is running (pid:$(checkpid))\n"
    else
        echo ""
        printf "$(date +%Y%m%d-%H:%M:%S) LiquidityWarning.jar is starting..."
        cd /app/mservice/CBTK_new
        /usr/java/jdk1.7.0_25/bin/java -jar LiquidityWarning.jar > /dev/null 2>&1 &

    fi

}

stop ()
{
    if [ $(checkpid) ] ; then
        kill -9 $(checkpid)
        echo -e "\n$(date +%Y%m%d-%H:%M:%S) LiquidityWarning.jar stop success\n"
    fi

}

status ()
{
        if [ $(checkpid) ] ; then
        echo -e "\n$(date +%Y%m%d-%H:%M:%S) LiquidityWarning.jar is running (pid:$(checkpid))\n"
    else    
        echo -e "\n$(date +%Y%m%d-%H:%M:%S) LiquidityWarning.jar is not started\n"
    fi
}

restart()
{
        if [ $(checkpid) ] ; then
                stop
                sleep 2
                start
        else
                echo -e "\n$(date +%Y%m%d-%H:%M:%S) LiquidityWarning.jar is not started\n"
        fi
}

case "$1" in
  start)
      start
      ;;
  stop)
      stop
      ;;
  restart)
      restart
      ;;
  status)
      status
      ;;
  *)
    echo -e "\nUsage: $0 {start|stop|status|restart|reload}\n"
      exit 1
      ;;
esac

这里有很多问题。echo-e 参数应该只在特定情况下使用(实际上,它与 echo 的 POSIX 规范不兼容 -- 请参见 http://pubs.opengroup.org/onlinepubs/009604599/utilities/echo.html,建议使用 printf %b 替代);此外,echo $(foo) 通常是运行 foo 的低效和有缺陷的方式。此外,在许多地方缺少引号;考虑通过 http://shellcheck.net 运行它。 - Charles Duffy
“ps | grep” 方法总的来说也是很危险的。"ps | grep LiquidityWarning.jar" 意味着你不仅杀死了服务,还杀死了包含该名称的管理命令,如“scp buildserver:/path/to/new/LiquidityWarning.jar .”。使用适当的进程监控系统比依赖名称匹配更安全。 - Charles Duffy
谢谢,我的脚本现在运行稳定了,我会改进我的脚本。 - Bình Nguyễn Quang

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接