当我使用Go的exec.Command{}
执行一些包含nohup的bash脚本时,它将永远挂起。
我不知道ping和ifconfig之间有什么区别。我试图重定向stdin(< /dev/null
),stdout(> /dev/null
)和stderr(2> /dev/null
)以及它们的组合,其中一些无法正常工作。
当我使用sh
执行脚本时,它会立即结束。
Go代码:
package main
import (
"fmt"
"os/exec"
)
func main() {
cmd := exec.Command("sh", "a.sh")
out, err := cmd.Output() // Or cmd.CombinedOutput()
fmt.Println(string(out), err)
}
Bash脚本(a.sh):
#!/bin/bash
# hangs
#nohup ping localhost &
# dot not hang
nohup ifconfig &
答案 0 :(得分:2)
(在固定错误的情况下转换评论以进行回答)
这里使用nohup
主要是red herring。真正的问题是ping
永远不会完成。但是,nohup
有一些额外的怪异之处,如果您从交互式终端运行这两组命令,则可以看到这些怪异之处:
$ nohup echo foo
nohup: ignoring input and appending output to 'nohup.out'
$ cat nohup.out
foo
$
vs:
$ nohup echo foo </dev/null 2>&1 | cat
foo
$
请注意第一个如何打印一条奇怪的消息,然后输出foo
进入文件;第二个没有,然后输出foo
出现在常规输出流上。这是因为POSIX认为nohup
应该在适当的情况下进行这些重定向。 1 与exec.Cmd
和cmd.Output
一起运行时,重定向不是 执行。
在操作系统级别上,在Linux或其他类似Unix的系统上,exec
代码创建一个OS pipe
object,调用的命令可以通过该https://golang.org/src/os/exec/exec.go#L280将输出发送回Go运行时。 (取决于您运行命令的方式,它的stderr输出可能有一个单独的管道,或者两个都可以定向到单个管道;请参见https://pubs.opengroup.org/onlinepubs/9699919799/utilities/nohup.html。)该管道最终被传递给{{1 }},以便ping
可以随时将输出写在那里。
外壳本身退出,因为命令ping
在后台。但是,nohup ping localhost &
仍然具有对管道对象的写访问权,因此Go运行时将继续调用OS ping
代码,直到关闭管道为止(从未如此)。如果管道曾经关闭过,那么Go运行时将收到EOF并调用read
系统调用以收集外壳程序的退出状态,但这永远不会发生。
重定向wait
的输出,以使外壳本身具有对管道的唯一写访问权,应导致在外壳本身退出后立即关闭管道。
(某些外壳可能具有内置的ping
,其行为可能很奇怪,尤其是在存在重定向的情况下。某些古老的外壳确实如此。)
1 有关完整的详细信息,请参见https://godoc.org/golang.org/x/crypto/ssh/terminal#IsTerminal。如果输入是终端,并且输出和stderr是终端,则Linux变体会重定向stdin以及stdout和stderr。 FreeBSD变体仅重定向stdout和/或stderr。 “是终端”测试基于C语言nohup
函数,其功能与Blueprints相同。