【问题标题】:Execute a timed function in bash在 bash 中执行定时函数
【发布时间】:2016-12-14 19:22:43
【问题描述】:

我正在尝试实现一个定时功能。如果计时器超时,则应终止该功能/命令。如果函数/命令完成,计时器不应让 bash 等待计时器超时。

(cmdpid=$BASHPID; \
    ( sleep 60; kill $cmdpid 2>/dev/null) &  \
    child_pid=$!; \
    ssh remote_host /users/jj/test.sh; \
    kill -9 $child_pid)

test.sh 可能会也可能不会在 60 秒内完成。这很好用。

但是当我想得到 test.sh 的结果时,它会回显“SUCESS”或“FAILURE”,我尝试了

result=$(cmdpid=$BASHPID; \
    ( sleep 60; kill $cmdpid 2>/dev/null) &  \
    child_pid=$!; \
    ssh remote_host /users/jj/test.sh; \
    kill -9 $child_pid)

在这里等待计时器退出。我可以看到使用 set -x 命令执行了“kill -9 $child_pid”,但是 kill 并不是真正杀死子 shell。

【问题讨论】:

  • 你说的是什么子shell?如果sleep 完成,kill $cmdpid 将杀死第一个子shell...如果仅使用 QUIT 信号(我不确定)是否可行。我的疑问:如果子shell 的任何子进程被挂起,是否可以使用 QUIT 信号杀死子shell?
  • 问题是您正在杀死子shell,而不是其中的sleep。如果您查看 ps,您会看到 sleep 的父 pid 为 0。
  • 您知道timeout 命令吗?我似乎在尝试重新发明轮子......
  • 使用timeout 很方便,但它不会杀死由脚本启动的独立子进程(例如)nohup /folder/OtherScript &。通过将计时器放在一个单独的脚本中,可以自定义计时器并杀死这些独立的子进程。
  • @cdarke:你是对的。内部子壳被杀死,但睡眠仍然存在。但是sleep的PID不是0,而是$child_pid+1。谷歌一点点告诉我,当一个进程被杀死时,只有它的直接子进程被发出信号。但是由于我正在杀死 sleep 的父级,所以新的父级是 1。

标签: bash


【解决方案1】:

解决这个问题的一种方法是在单独的脚本上运行计时器,比如MyTimerTest,它从(比如)MainScriptTest 调用但单独运行,然后无论哪个脚本首先完成“杀死”另一个。例如:

MainScriptTest 上,您可以将其放在开头:

nohup /folder/MyTimerTest > /dev/null 2>&1 &

MainScriptTest 你可以把它放在最后:

killall MyTimerTest > /dev/null 2>&1

MyTimerTest 可能是这样的:

#!/bin/bash
sleep 60
killall MainScriptTest > /dev/null 2>&1
exit 0

注意:混合大写和小写字母的脚本的长名称(例如:MainScriptTest)是故意的,killall 区分大小写,这有助于防止它杀死它不应该杀死的东西。为了非常安全,您甚至可能希望在更长的名称之外添加一个标记,例如:MainScriptTest88888 或类似的东西。

编辑:感谢gilez,他建议使用timeout 命令。如果您的系统上可以使用该功能,则可以像这样进行快速单行:

timeout 60 bash -c "/folder/MainScriptTest"

使用timeout 很方便。但是,如果MainScriptTest 创建了独立的子进程(例如通过调用:nohup /folder/OtherScript &),那么timeout 不会杀死这些子进程,并且退出不会是干净的。

我给出的第一个解决方案更长,但可以通过将它们添加到MainScriptTest 来定制以杀死这些子进程(或您想要的任何其他进程),例如:

killall OtherScript > /dev/null 2>&1

【讨论】:

  • 这不起作用。从新的 bash 脚本运行的 sleep 假定名称为“sleep”,而 killall 找不到它
  • 我无法在我的系统上复制您的问题 - 在这里它可以完美运行。我有GNU bash, version 4.3.30(1)。我也不确定我是否理解你所说的问题所在。是sleep 一个人继续运行吗? MainScriptTestMyTimerTest 呢,他们会被杀吗?再次,如果您想对此进行调查,请详细说明——如果您愿意,我想这样做,这样做会很有趣并且可能具有指导意义。
  • 当我尝试你提到的方式时,“ps”显示的是“sleep”而不是“MyTimerTest”。所以我无法使用“killall MyTimerTest”杀死脚本。我使用的 bash 是 4.1.2(1)。不幸的是,我无法升级它。
  • 对不起,但这对我来说没有意义。你为什么提到pskillall 根本不需要这样做,我的代码也不需要。此外,如果 ps aux | grep MyTimerTest | grep -v "grep MyTimerTest" 没有显示任何内容,则 MyTimerTest 已经没有运行。请记住,MyTimerTest 会在 60 秒后退出。
  • 我正在尝试调试它。我看到的问题是,即使 MainScriptTest 完成其工作并退出,我也没有得到提示。所以使用另一个shell,我试图查看所有进程正在运行。这是在 60 秒内完成的,我预计 MyTimerTest 会运行。但是我在“ps”中看到的是睡眠,而不是MyTimerTest。由于 killall 试图杀死名为 MyTimterTest 的进程,因此无法杀死。
【解决方案2】:

找到了其他方法。

result=$( ssh $remote_host /users/jj/test.sh ) & mypid=$!
( sleep 10; kill -9 $mypid ) &
wait $mypid

【讨论】:

    猜你喜欢
    • 2018-04-22
    • 1970-01-01
    • 1970-01-01
    • 2016-01-31
    • 1970-01-01
    • 1970-01-01
    • 2022-11-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多