doufu7835 2019-08-25 06:50
浏览 270

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

When I use Go's exec.Command{} to execute some bash script that contains nohup, it will hang forever.

I don't know what are the differences between ping and ifconfig. I tried to redirect the stdin (< /dev/null), the stdout(> /dev/null) and the stderr(2> /dev/null), and their combination, some of them work some don't.

When I use sh to execute the script, it just ends up immediately.

The Go code:

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)
}

The Bash script (a.sh):

#!/bin/bash

# hangs
#nohup ping localhost &

# dot not hang
nohup ifconfig &
  • 写回答

1条回答 默认 最新

  • douhoulei4706 2019-08-25 09:14
    关注

    (Converting comments, with glitches fixed, to answer)

    The use of nohup here is mostly a red herring. The real problem is that ping never finishes. However, nohup has some extra weirdness, which you can see if you run, from an interactive terminal, these two sets of commands:

    $ 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
    $ 
    

    Note how the first one printed a weird message, and then the output foo went to a file; the second did not, and then the output foo showed up on the regular output stream. This is because POSIX says that nohup should do these redirections if appropriate.1 When run with exec.Cmd and cmd.Output, the redirections are not performed.

    At the OS level, on a Linux- or other Unix-like system, the exec code creates an OS pipe object by which the invoked command can send output back to the Go runtime. (There may be a separate pipe for its stderr output, or the two may both be directed to a single pipe, depending on how you run the command; see https://golang.org/src/os/exec/exec.go#L280.) This pipe winds up being passed to ping, so that ping can keep writing output there as long as it likes.

    The shell itself exits, because the command nohup ping localhost & is backgrounded. However, ping still has write access to the pipe object, so the Go runtime continues calling the OS read code until the pipe is closed—which is never. If the pipe were ever closed, the Go runtime would receive EOF and call the wait system call to collect the shell's exit status, but that never happens.

    Redirecting ping's output, such that the shell itself has the only write access to the pipe, should result in the pipe being closed as soon as the shell itself exits.

    (Some shells may have a builtin nohup that may behave weirdly, especially in the presence of redirection. This is true of some particularly ancient shells.)


    1See https://pubs.opengroup.org/onlinepubs/9699919799/utilities/nohup.html for complete details. The Linux variant redirects stdin as well as stdout and stderr, if the input is a terminal, and if the output and stderr are terminals. The FreeBSD variant redirects only stdout and/or stderr. The "is a terminal" test is based on the C language isatty function, which does the same thing as https://godoc.org/golang.org/x/crypto/ssh/terminal#IsTerminal.

    评论

报告相同问题?

悬赏问题

  • ¥15 opencv图像处理,需要四个处理结果图
  • ¥15 无线移动边缘计算系统中的系统模型
  • ¥15 深度学习中的画图问题
  • ¥15 java报错:使用mybatis plus查询一个只返回一条数据的sql,却报错返回了1000多条
  • ¥15 Python报错怎么解决
  • ¥15 simulink如何调用DLL文件
  • ¥15 关于用pyqt6的项目开发该怎么把前段后端和业务层分离
  • ¥30 线性代数的问题,我真的忘了线代的知识了
  • ¥15 有谁能够把华为matebook e 高通骁龙850刷成安卓系统,或者安装安卓系统
  • ¥188 需要修改一个工具,懂得汇编的人来。