Bash中的&&运算符可以防止通过SSH后台化进程。

7
在尝试弄清楚为什么一个Capistrano任务(试图在后台启动守护进程)挂起之后,我发现在bash over ssh中使用&&会阻止后续程序在后台运行。我已经在bash 4.1.5和4.2.20上尝试过了。
以下命令将在bash中挂起(即等待sleep完成):
ssh localhost "cd /tmp && nohup sleep 10 >/dev/null 2>&1 &"

以下内容不会生效:
ssh localhost "cd /tmp ; nohup sleep 10 >/dev/null 2>&1 &"

这也不会:
cd /tmp && nohup sleep 10 >/dev/null 2>&1 &

无论是使用&&还是ssh,zsh和dash都会在所有情况下将其后台执行。这对于bash来说是正常/预期的行为,还是一个bug呢?


1
我不确定如何回答你的问题,但我会说在使用SSH连接到远程端时,有时也会遇到类似的问题。我不知道这个问题是否只与Bash有关,但在某些情况下,使用Bash shell的disown内置命令可能比nohup命令更好用。如果你感兴趣,可以在运行Bash时输入help disown | less来了解更多信息。如果你碰巧找到了一般性的解决方案,请在此发布。我很想看看。 - thb
我实际上尝试了各种组合的 disownnohup 和重定向,但都没有成功。&& 始终阻止后台运行。我可以通过将两个命令包装在子 shell 中来解决问题,但那并不是真正的解决方法。 - Hinrik
这个命令在我这里似乎可以工作。你所说的“挂起”是什么意思?cd 命令执行需要很长时间吗?这可能是远程服务器的问题,例如负载过重。 - suvayu
我不知道OP所说的“hangs”的意思,但在我的情况下,尝试注销从未完成过。无论如何,这是我的经验。不知道为什么。 - thb
“Hangs”的意思是它实际上会等待sleep完成后才返回。 - Hinrik
2个回答

4

一个简单的解决方案是使用:

ssh localhost "(cd /tmp && nohup sleep 10) >/dev/null 2>&1 &"

(如果你使用花括号,下面的第二个例子也适用).

我没有进一步尝试,但我相信这与未关闭的文件描述符有关。也许zsh和dash将&&绑定在了必须拼写为以下内容的位置:

{ cd /tmp && nohup sleep 10; } >/dev/null 2>&1

在bash中。


不行,在dash中进行的快速实验表明echo foo && echo bar >file只重定向后者。 仍然,这必须与残留的打开fd有关,导致ssh等待更多输出;我过去经常遇到这个问题。
还有一个技巧,在这种特定情况下,如果您使用括号或大括号,则不需要,但在更一般的情况下可能有用,其中与&&相关的命令集更复杂。 由于bash似乎不适当地保留了&&中的文件描述符,但是没有使用;,因此可以将a && b && c转换为a || exit 1; b || exit 1; c。 这适用于测试案例:

ssh localhost "true || exit 1; echo going on; nohup sleep 10 >/dev/null 2>&1 &"

true 替换为 false 就可以省略"going on"的输出。

(你也可以使用 set -e, 但有时这是一个过于强劲的工具。)


是的,用|| exit 1;替换&&绝对是要使用的解决方法。 - Hinrik

1

这似乎有效:

ssh localhost "(exec 0>&- ; exec 1>&-; exec 2>&-; cd /tmp; sleep 20&)"

1
关闭所有描述符的缺点在于无法将错误消息发送回源(ssh)主机。 - torek

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