【问题标题】:Start detached command with redirect to file使用重定向到文件启动分离命令
【发布时间】:2016-10-25 14:53:51
【问题描述】:

我正在尝试在一个分离的进程中启动一个命令,以便在 go 程序退出后它可以继续。我需要将命令的输出重定向到一个文件。

我需要的是这样的:

func main() {
    command := exec.Command("/tmp/test.sh", ">", "/tmp/out")

    if err := command.Start(); err != nil {
        fmt.Fprintln(os.Stderr, "Command failed.", err)
        os.Exit(1)
    }

    fmt.Println("Process ID:", command.Process.Pid)
}

显然这种重定向不起作用。由于我在启动长时间运行的命令后立即退出程序,因此无法打开文件并将其绑定到 Stdout。

有没有办法实现这样的重定向?

【问题讨论】:

    标签: go process io


    【解决方案1】:

    你可以启动一个shell来执行你的命令/应用程序,你可以将它的输出重定向到一个文件。即使您的 Go 应用退出,shell 也会继续运行并执行您的脚本/应用。

    例子:

    cmd := exec.Command("sh", "-c", "/tmp/test.sh > /tmp/out")
    if err := cmd.Start(); err != nil {
        panic(err)
    }
    fmt.Println("Process ID:", cmd.Process.Pid)
    

    使用这个简单的 Go 应用程序对其进行测试(将 /tmp/test.sh 替换为您编译成的可执行二进制文件的名称):

    package main
    import ("fmt"; "time")
    func main() {
        for i := 0; i < 10; i++ {
            fmt.Printf("%d.: %v\n", i, time.Now())
            time.Sleep(time.Second)
        }
    }
    

    这个应用程序每秒简单地在标准输出中打印一行。您可以查看输出文件的写入方式,例如tail -f /tmp/out

    请注意,您可以根据自己的喜好(以及 test.sh 脚本的规定)使用其他 shell 来执行脚本。

    例如使用bash:

    cmd := exec.Command("/bin/bash", "-c", "/tmp/test.sh > /tmp/out")
    // rest is unchanged
    

    请注意,要由 shell 执行的命令作为单个 string 参数传递,它不会像您直接在命令提示符中执行时那样分解为多个。

    【讨论】:

    • 感谢您的回答。虽然你的没有问题,但我会接受@thoeni 的回答,因为它是 windows 和 linux 的单一解决方案,对我来说更方便。
    • 如果我们需要使用 os 提供的 stdOut 和文件重定向,这很好,触发后不需要在实际进程之上运行另一个 go 进程。谢谢
    【解决方案2】:

    也许你可以试试这个:https://stackoverflow.com/a/28918814/2728768

    打开一个文件(os.File 实现io.Writer),然后将它作为command.Stdout 传递就可以了:

    func main() {
        command := exec.Command("./tmp/test.sh")
        f, err := os.OpenFile("/tmp/out", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
        if err != nil {
            fmt.Printf("error opening file: %v", err)
        }
        defer f.Close()
        // On this line you're going to redirect the output to a file
        command.Stdout = f
        if err := command.Start(); err != nil {
            fmt.Fprintln(os.Stderr, "Command failed.", err)
            os.Exit(1)
        }
    
        fmt.Println("Process ID:", command.Process.Pid)
    }
    

    不确定这是否适合您的案例。我已经在本地尝试过,它似乎可以工作...请记住,您的用户应该能够创建/更新文件。

    【讨论】:

    • 这行得通,但我不明白为什么。进程关闭后如何写入文件?如果这无关紧要,为什么我们还需要打开它?
    • 我没想到。所以,我会调查并回复你,但我正在阅读文档和 exec.Cmd 和我们重定向的标准输出是由一个作家描述符管理的,我相信它将负责关闭它(它看起来像也打开它...):golang.org/src/os/exec/exec.go?s=6559:7169#L231
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-03-12
    • 1970-01-01
    • 2010-10-10
    • 2018-06-07
    • 1970-01-01
    相关资源
    最近更新 更多