exec.Command挂在包含nohup的Bash脚本上

时间:2019-08-25 06:50:45

标签: bash go

当我使用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 &

1 个答案:

答案 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.Cmdcmd.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相同。