如果进程死了,如何编写 bash 脚本来重新启动进程?

     2023-03-04     270

关键词:

【中文标题】如果进程死了,如何编写 bash 脚本来重新启动进程?【英文标题】:How do I write a bash script to restart a process if it dies? 【发布时间】:2010-10-16 08:38:07 【问题描述】:

我有一个 python 脚本,它将检查队列并对每个项目执行操作:

# checkqueue.py
while True:
  check_queue()
  do_something()

如何编写一个 bash 脚本来检查它是否正在运行,如果没有,则启动它。大致如下伪代码(或者它应该做类似ps | grep的东西?):

# keepalivescript.sh
if processidfile exists:
  if processid is running:
     exit, all ok

run checkqueue.py
write processid to processidfile

我会从 crontab 中调用它:

# crontab
*/5 * * * * /path/to/keepalivescript.sh

【问题讨论】:

只是为 2017 添加这个。使用 supervisord。 crontab 并不是要执行此类任务。 bash 脚本在发出真正的错误时很糟糕。 ***.com/questions/9301494/… 使用 inittab 和 respawn 代替其他非系统解决方案怎么样?见superuser.com/a/507835/116705 【参考方案1】:

避免使用 PID 文件、crons 或任何其他试图评估不是其子进程的进程。

在 UNIX 中有一个很好的理由,您只能等待您的孩子。任何试图解决这个问题的方法(ps 解析、pgrep、存储 PID,...)都是有缺陷的,并且存在漏洞。直接说

相反,您需要将监控您的进程的进程作为该进程的父进程。这是什么意思?这意味着只有您的进程启动的进程才能可靠地等待它结束。在 bash 中,这绝对是微不足道的。

until myserver; do
    echo "Server 'myserver' crashed with exit code $?.  Respawning.." >&2
    sleep 1
done

上面的 bash 代码在 until 循环中运行 myserver。第一行从myserver 开始并等待它结束。当它结束时,until 检查它的退出状态。如果退出状态是0,这意味着它优雅地结束了(这意味着你要求它以某种方式关闭,并且它成功地关闭了)。在这种情况下,我们不想重新启动它(我们只是要求它关闭!)。如果退出状态是 not 0until 将运行循环体,它会在 STDERR 上发出错误消息并重新启动循环(回到第 1 行)1 秒后。

我们为什么要等一秒钟?因为如果myserver 的启动顺序出现问题并且它立即崩溃,您将有一个非常密集的循环不断重启和崩溃。 sleep 1 消除了压力。

现在您需要做的就是启动这个 bash 脚本(可能是异步的),它会监控 myserver 并在必要时重新启动它。如果您想在启动时启动监视器(使服务器“存活”重新启动),您可以使用 @reboot 规则在用户的 cron(1) 中安排它。使用crontab 打开您的 cron 规则:

crontab -e

然后添加一个规则来启动你的监控脚本:

@reboot /usr/local/bin/myservermonitor

或者;查看 inittab(5) 和 /etc/inittab。您可以在其中添加一行以使 myserver 从某个初始级别开始并自动重生。


编辑。

让我补充一些关于为什么使用 PID 文件的信息。虽然它们很受欢迎;他们也有很大的缺陷,你没有理由不以正确的方式去做。

考虑一下:

    PID回收(杀错进程):

    /etc/init.d/foo start:启动foo,将foo的PID写入/var/run/foo.pid 过了一会儿:foo 莫名其妙地死了。 稍后:任何启动的随机进程(称为 bar)都会使用随机 PID,想象一下它会使用 foo 的旧 PID。 你注意到foo已经消失了:/etc/init.d/foo/restart读取/var/run/foo.pid,检查它是否还活着,找到bar,认为它是foo,杀死它,开始一个新的foo

    PID 文件过时。您需要过于复杂(或者我应该说是不平凡的)逻辑来检查 PID 文件是否过时,并且任何此类逻辑都容易受到1. 的攻击。

    如果您甚至没有写入权限或处于只读环境中怎么办?

    这是毫无意义的过度复杂化;看看我上面的例子是多么简单。完全不需要复杂化。

另请参阅:Are PID-files still flawed when doing it 'right'?

顺便说一句; 比 PID 文件更糟糕的是解析 ps永远不要这样做。

    ps 非常不便携。虽然您几乎可以在每个 UNIX 系统上找到它;如果您想要非标准输出,它的参数会有很大差异。而且标准输出仅供人类使用,不能用于脚本解析! 解析ps 会导致大量误报。以ps aux | grep PID 为例,现在想象有人以某处的数字作为参数启动一个进程,该参数恰好与您盯着守护进程使用的 PID 相同!想象一下,两个人开始一个 X 会话,而你正在寻找 X 来杀死你的会话。就是各种不好。

如果您不想自己管理流程;有一些非常好的系统可以作为您流程的监控器。例如,查看runit。

【讨论】:

@Chas。欧尼斯:我认为没有必要。它只会无缘无故地使实施复杂化。简单总是更重要;如果它经常重新启动,睡眠将使它不会对您的系统资源产生任何不良影响。反正已经有消息了。 @orschiro 程序运行时没有资源消耗。如果它在启动时立即存在,持续存在,睡眠 1 的资源消耗仍然完全可以忽略不计。 可以相信我只是看到了这个答案。非常感谢! @TomášZato 您可以在不测试进程退出代码while true; do myprocess; done 的情况下执行上述循环,但请注意,现在无法停止进程。 @SergeyP.akaazure 强制父母在 bash 退出时杀死孩子的唯一方法是将孩子变成工作并发出信号:trap 'kill $(jobs -p)' EXIT; until myserver & wait; do sleep 1; done【参考方案2】:

看看 monit (http://mmonit.com/monit/)。它处理脚本的启动、停止和重新启动,并且可以进行运行状况检查以及必要时重新启动。

或者做一个简单的脚本:

while true
do
/your/script
sleep 1
done

【讨论】:

Monit 正是您想要的。 "while 1" 不起作用。您需要“while [1]”或“while true”或“while :”。见unix.stackexchange.com/questions/367108/what-does-while-mean【参考方案3】:

最简单的方法是使用flock on file。在 Python 脚本中你会做

lf = open('/tmp/script.lock','w')
if(fcntl.flock(lf, fcntl.LOCK_EX|fcntl.LOCK_NB) != 0): 
   sys.exit('other instance already running')
lf.write('%d\n'%os.getpid())
lf.flush()

在 shell 中你可以实际测试它是否正在运行:

if [ `flock -xn /tmp/script.lock -c 'echo 1'` ]; then 
   echo 'it's not running'
   restart.
else
   echo -n 'it's already running with PID '
   cat /tmp/script.lock
fi

当然你不必测试,因为如果它已经在运行并且你重新启动它,它会以'other instance already running'退出

当进程终止时,它的所有文件描述符都将关闭,所有锁都会自动移除。

【讨论】:

这可以通过删除 bash 脚本来简化它。如果 python 脚本崩溃会发生什么?文件解锁了吗? 一旦应用程序停止,文件锁就会被释放,无论是自然终止还是崩溃。 @Tom ...更准确地说——一旦文件句柄关闭,锁就不再处于活动状态。如果 Python 脚本从未有意关闭文件句柄,并确保它不会通过垃圾收集的文件对象自动关闭,那么它关闭可能意味着脚本退出/被杀死。这甚至适用于重新启动等。 有很多更好的方法来使用flock...事实上,手册页明确地展示了如何使用! exec lock_fd>/tmp/script.lock; flock -x "$lock_fd" 是相当于您的 Python 的 bash,并保持锁定状态(因此,如果您随后执行进程,锁定将一直保持到该进程退出)。 我对你投了反对票,因为你的代码是错误的。使用flock 是正确的方法,但你的脚本是错误的。您需要在 crontab 中设置的唯一命令是:flock -n /tmp/script.lock -c '/path/to/my/script.py'【参考方案4】:

您应该使用 monit,这是一个标准的 unix 工具,可以监控系统上的不同事物并做出相应的反应。

来自文档:http://mmonit.com/monit/documentation/monit.html#pid_testing

使用 pidfile /var/run/checkqueue.pid 检查进程 checkqueue.py 如果更改了 pid 然后 exec "checkqueue_restart.sh"

您还可以配置 monit 在重启时向您发送电子邮件。

【讨论】:

Monit 是一个很棒的工具,但它不是在正式意义上被指定为 POSIX 或 SUSV 的标准。【参考方案5】:
if ! test -f $PIDFILE || ! psgrep `cat $PIDFILE`; then
    restart_process
    # Write PIDFILE
    echo $! >$PIDFILE
fi

【讨论】:

酷,这很好地充实了我的一些伪代码。两个qns:1)我如何生成PIDFILE? 2)什么是psgrep?它不在 ubuntu 服务器上。 ps grep 只是一个与ps ax|grep ... 功能相同的小应用程序。您可以安装它或为此编写一个函数: function psgrep() ps ax|grep -v grep|grep -q "$1" 刚刚注意到我没有回答你的第一个问题。 在非常繁忙的服务器上,PID 可能会在您检查之前被回收。【参考方案6】:

在线:

while true; do <your-bash-snippet> && break; done

例如#1

while true; do openconnect x.x.x.x:xxxx && break; done

例如#2

while true; do docker logs -f container-name; sleep 2; done

【讨论】:

这是我最喜欢的答案,内联效果很好,没有额外的软件依赖,我想要这个命令形式,让我们称之为 jafari 这是我使用它时的真实情况;做 ffmpeg -f x11grab -framerate 30 -video_size 1920x1080 -i :10.0 -f mpegts srt://:6666?mode=listener && break;完成应该有 jafari ffmpeg -f x11grab -framerate 30 -video_size 1920x1080 -i :10.0 -f mpegts srt://:6666?mode=listener【参考方案7】:

我不确定它在操作系统之间的可移植性如何,但您可以检查您的系统是否包含“run-one”命令,即“man run-one”。 具体来说,这组命令包括“run-one-constantly”,这似乎正是我们所需要的。

来自手册页:

不断运行命令 [ARGS]

注意:显然这可以从您的脚本中调用,但它也完全不需要脚本。

【讨论】:

与公认的答案相比,这有什么优势吗? 是的,我认为最好使用内置命令,而不是编写一个 shell 脚本来执行与系统代码库的一部分相同的事情。即使需要该功能作为 shell 脚本的一部分,也可以使用上述命令,因此它与 shell 脚本问题相关。 这不是“内置”的;如果它默认安装在某个发行版上,您的答案可能应该指定发行版(如果您的不是其中之一,理想情况下包括一个指向哪里下载它的指针)。 看起来像是一个 Ubuntu 实用程序;但即使在 Ubuntu 上也是可选的。 manpages.ubuntu.com/manpages/bionic/man1/run-one.1.html 值得注意的是:run-one 实用程序正如其名称所说的那样 - 您只能运行使用 run-one-nnnnn 运行的任何命令的一个实例。这里的其他答案与可执行文件无关——根本不关心命令的内容。【参考方案8】:

我已经在许多服务器上成功使用了以下脚本:

pid=`jps -v | grep $INSTALLATION | awk 'print $1'`
echo $INSTALLATION found at PID $pid 
while [ -e /proc/$pid ]; do sleep 0.1; done

注释:

它正在寻找一个java进程,所以我 可以使用jps,这更多 跨分布一致 ps $INSTALLATION 包含足够多的进程路径,这完全是明确的 在等待进程终止时使用睡眠,避免占用资源:)

此脚本实际上用于关闭正在运行的 tomcat 实例,我想在命令行中关闭(并等待)它,因此将它作为子进程启动根本不适合我。

【讨论】:

grep | awk 仍然是antipattern - 你想让awk "/$INSTALLATION/ print \$1 " 把没用的grep 合并到Awk 脚本中,它可以很好地通过正则表达式本身找到行,非常感谢.【参考方案9】:

我将它用于我的 npm 进程

#!/bin/bash
for (( ; ; ))
do
date +"%T"
echo Start Process
cd /toFolder
sudo process
date +"%T"
echo Crash
sleep 1
done

【讨论】:

【参考方案10】:
watch "yourcommand"

如果/当它停止时(延迟 2 秒后),它将重新启动进程。

watch -n 0.1 "yourcommand"

在 0.1 秒后重新启动,而不是默认的 2 秒

watch -e "yourcommand"

如果程序因错误退出而停止重新启动。

优点:

内置命令 一行 易于使用和记忆。

缺点:

只有在命令完成后才在屏幕上显示命令的结果

【讨论】:

这似乎不准确,“观察 - 定期执行程序”,这意味着它将每 xx 秒执行一次,而不是如果/当进程停止时。 @smartins 根据文档,延迟是一个间隔。因此,使用-n 5,它将在最后一个停止后 5 秒再次运行该命令。您可以使用watch -n 5 "sleep 5" 对其进行测试,并查看它每 10 秒更新一次。

如何启动不是原始进程子进程的新进程?

...做的是执行一些命令,等待30秒,然后再执行一些命令。如果我让我 查看详情

在启动时启动 java 进程并在死亡时自动重启

...15:34:48【问题描述】:我对我的Java应用程序有两个要求。如果它死了,请重新启动它。如果服务器重新启动,请重新启动它-很简单。使用答案here我有一个脚本,当java应用程序死掉时会重新启动。#!/bin/bash直到java-Xms256m-Xmx7 查看详情

如果找不到则检查进程并重新启动程序的脚本[重复]

】如果找不到则检查进程并重新启动程序的脚本[重复]【英文标题】:Scripttocheckforprocess&restartprogramifnotfound[duplicate]【发布时间】:2016-09-2621:35:15【问题描述】:我正在使用这个检查脚本来查看一个名为CCcam的包是否正在运行... 查看详情

如何编写 bash 脚本来设置全局环境变量?

】如何编写bash脚本来设置全局环境变量?【英文标题】:Howtowriteabashscripttosetglobalenvironmentvariable?【发布时间】:2012-09-0307:41:28【问题描述】:最近写了一个设置环境变量的脚本,看看:#!/bin/bashecho"Passapath:"readpathecho$pathdefaultPath... 查看详情

在bash脚本中获取命令的子进程

】在bash脚本中获取命令的子进程【英文标题】:getsubprocessofcommanadinsidebashscript【发布时间】:2015-11-2616:28:25【问题描述】:我正在尝试编写一个脚本来获取从bash脚本启动的进程的名称和pid。示例:#!/bin/bashpass=(`command1|grep....|tail... 查看详情

如何编写一个 Linux bash 脚本来告诉我 LAN 中哪些计算机处于打开状态?

...一个LinuxBash脚本来告诉我LAN中哪些计算机处于开启状态?如果我可以给它一个IP地址范围作为输入,那会很有帮助。【问题讨论】:使用Nagi 查看详情

批量检测程序无响应

...正在尝试编写一个批处理脚本来检测.exe是否没有响应,如果没有响应,它将运行一段代码来杀死它,然后还会执行其他一些操作。我知道如果进程没有在一行中响应,我可以如何终止进程并重新启动它,但我不确定我能做的不... 查看详情

在 Bash 脚本中,如果发生某种情况,如何退出整个脚本?

】在Bash脚本中,如果发生某种情况,如何退出整个脚本?【英文标题】:InaBashscript,howcanIexittheentirescriptifacertainconditionoccurs?【发布时间】:2010-11-2514:00:05【问题描述】:我正在用Bash编写一个脚本来测试一些代码。但是,如果首... 查看详情

linux下如何监听进程

在linux下,监听进程状态是运行的还是等待的还是已经死了的。同时监听由该进程启动的子进程状态。一、superviseSupervise是daemontools的一个工具,可以用来监控管理unix下的应用程序运行情况,在应用程序出现异常时,supervise可以... 查看详情

怎么用c++编写一个windows服务程序来监控另一个程序,崩溃后重新启动。

可以发代码吗参考技术Ac++或者java都有方法能控制电脑进程,可以根据监控程序进程来进行重启,可以百度(c++如何查看电脑进程并重新启动)本回答被提问者采纳 查看详情

linux中如何执行脚本?

...ile.sh;执行脚本有三种方法:1../file.sh:特点:开启bash子进程来执行,也就是开启额外的进程来进行,不影响原进程的变量、配置等2.bashfile.sh特点:和./file.sh相同3.sourcefile.sh或者.file.sh特点:在原bash进程中执行脚本。第三种方... 查看详情

如何在 unix 中守护任意脚本?

...想处理两种常见的情况:我有一个应该永远运行的脚本。如果它死了(或在重新启动时),请重新启动它。不要让两个副本同时运行(检测一个副本是否已经在运行,在这种情况下不要启动它)。我有一个简单的 查看详情

如何避免docker容器启动脚本运行后自动退出

...器启动脚本运行后自动退出的解决办法dockerrun指定的命令如果不是那些一直挂起的命令(比如运行top,不断echo),就是会自动退出的。-d命令是设置detach为true,根据官方的文档,意思是让这个命令在后台运行,但并不是一直运... 查看详情

如何创建一个 bash 脚本来检查 SSH 连接?

】如何创建一个bash脚本来检查SSH连接?【英文标题】:HowtocreateabashscripttochecktheSSHconnection?【发布时间】:2010-11-2704:19:20【问题描述】:我正在创建一个bash脚本,该脚本将登录到远程机器并创建私钥和公钥。我的问题是远程机器... 查看详情

如何编写在程序上执行 gdb 的 bash 脚本

】如何编写在程序上执行gdb的bash脚本【英文标题】:Howtowriteabashscriptthatexecutesgdbonaprogram【发布时间】:2020-04-0420:29:32【问题描述】:我正在从http://www.cis.syr.edu/~wedu/seed/Labs_12.04/Software/Buffer_Overflow/Buffer_Overflow.pdf重新创建缓冲区... 查看详情

linux中如何执行脚本?

...odu+xfile.sh执行脚本有三种方法:1../file.sh特点:开启bash子进程来执行,也就是开启额外的进程来进行,不影响原进程的变量、配置等2.bashfile.sh特点:和./file.sh相同3.sourcefile.sh或者.file.sh特点:在原bash进程中执行脚本。第三种方... 查看详情

python脚本的守护进程与新贵

...启动后连续运行,当我需要更新其他模块时需要停止它。如果模块已崩溃或未运行,我可能会使用monit重新启动它。我正在使用不同的技术,例如Daemon、Upstart和许多其他技术。最好的方法是让我在所有新模块中都使用这种方法来... 查看详情

如何在 Bash 中运行超时的进程? [复制]

】如何在Bash中运行超时的进程?[复制]【英文标题】:HowtorunaprocesswithatimeoutinBash?[duplicate]【发布时间】:2012-04-3018:46:37【问题描述】:可能重复:Bashscriptthatkillsachildprocessafteragiventimeout有没有办法编写一个shell脚本来执行某个命... 查看详情