【问题标题】:Bash script that will survive disconnection, but not user breakBash 脚本将在断开连接后幸存,但不会在用户中断时幸存
【发布时间】:2013-11-14 00:29:04
【问题描述】:

我想编写一个 bash 脚本,如果用户断开连接,它将继续运行,但如果用户按 Ctrl+C 则可以中止。

我可以这样解决第一部分:

#!/bin/bash
cmd='
#commands here, avoiding single quotes...
'
nohup bash -c "$cmd" &
tail -f nohup.out

但是按 Ctrl+C 显然只会杀死 tail 进程,而不是主体。我可以两者兼得吗?也许使用屏幕?

【问题讨论】:

  • 正如您提到的屏幕:您当然可以在屏幕会话中运行您的代码,并bind Ctrl+C 到一些退出您的程序的代码(或者只是终止屏幕会话中的所有内容如果您不需要正常关机,请使用它)。然而,取决于终端模拟器,Ctrl+X 甚至永远不会到达屏幕。
  • 那么屏幕会话的启动必须在 bash 脚本本身中。 (基本上,我想要一个运行冗长更新过程的脚本,它的持续时间通常比用户预期的要长。)
  • 没问题。只需运行 screen 并让它运行另一个执行代码的 bash 实例。 (例如screen bash -c $cmd)。使用screen的-c参数指定一个临时文件,里面有bind命令来绑定Ctrl+C。

标签: bash gnu-screen nohup


【解决方案1】:

我想编写一个 bash 脚本,如果用户断开连接,该脚本将继续运行,但如果用户按 Ctrl+C 则可以中止。

我认为这正是你提出的问题的答案,这个没有屏幕:

#!/bin/bash
cmd=`cat <<EOF
# commands here
EOF
`
nohup bash -c "$cmd" &

# store the process id of the nohup process in a variable
CHPID=$!        

# whenever ctrl-c is pressed, kill the nohup process before exiting
trap "kill -9 $CHPID" INT

tail -f nohup.out

但是请注意,nohup 并不可靠。当调用用户注销时,nohup 很可能也会立即退出。在这种情况下 disown 效果更好。

bash -c "$cmd" &
CHPID=$!
disown

【讨论】:

  • “当调用用户注销时,很可能 nohup 也会立即退出” - 真的吗?我从来没有见过这个。你在考虑什么平台?
  • 我在 Debian 和 Ubuntu 上都有这个。 nohupped 程序是调用它的 shell 的子程序。当退出 shell 时,孩子们被终止了……然而,孙辈们留下来成为孤儿(并被 init 收集)。 Nohup 在许多情况下都不起作用,例如在后台启动一个带有 nohup 的程序,然后注销会杀死该程序。甚至 (( prog & ) & ) 也不起作用。我不得不手动取消进程。
  • 奇怪,我主要使用Ubuntu,没见过这个。我通常做nohup ./script.sh &amp;。不过感谢您的警告。
  • 哇,警告其他用户:如果您正在使用此脚本,请确保不要将其包装在另一个 nohup 中。我有一个超级脚本运行一堆其他脚本,然后是这个,并且没有运行超级脚本。内部是拖尾 nohup.out - 它产生输出到外部,它将输出写入 nohup.out,它被拖尾......产生更多输出...... gack。
【解决方案2】:

这可能是使用屏幕最简单的形式:

screen -S SOMENAME script.sh

然后,如果您断开连接,在重新连接时只需运行:

screen -r SOMENAME

Ctrl+C 应该会继续按预期工作

【讨论】:

    【解决方案3】:

    事实 1:当终端(例如 xterm)关闭时,shell 应该向其中运行的任何进程发送 SIGHUP(“挂断”)。这可以追溯到模拟调制解调器的时代,如果你在线时妈妈碰巧拿起电话,程序需要自行清理。信号可能会被捕获,因此可以使用特殊功能进行清理(关闭文件、删除临时垃圾等)。即使我们使用套接字和 SSH 隧道而不是模拟调制解调器,“失去连接”的概念仍然存在。 (概念不会改变;所有改变的只是我们用来实现它们的技术。)

    事实2Ctrl-C的效果取决于你的终端设置。通常,它会发送一个 SIGINT,但您可以通过在 shell 中运行 stty -a 并寻找“intr”来检查。

    您可以使用 bash 的 trap 命令来利用这些事实。例如,尝试在窗口中运行它,然后按 Ctrl-C 并检查 /tmp/trapped 的内容。然后再次运行,关闭窗口,再次查看/tmp/trapped的内容:

    #!/bin/bash
    
    trap "echo 'one' > /tmp/trapped" 1
    trap "echo 'two' > /tmp/trapped" 2
    
    echo "Waiting..."
    sleep 300000
    

    有关信号的信息,您应该可以man signal(FreeBSD 或 OSX)或man 7 signal(Linux)。

    (对于加分:看看我是如何给我的事实编号的?你明白为什么吗?)

    所以……你的问题。为了“幸存”断开连接,您需要指定脚本捕获 SIGHUP 时将运行的行为。

    (额外问题 #2:现在你明白 nohup 的名字是从哪里来的了吗?)

    【讨论】:

    • 谢谢,这很有用。通过测试你的脚本,似乎 SIGINT 被“困住”了,但仍然杀死了脚本。断开“陷阱”SIGHUP,但脚本继续运行。这是预期的吗?如果你想阻止 SIGINT 出于某种原因杀死脚本,你会怎么做?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-22
    • 1970-01-01
    相关资源
    最近更新 更多