开始一个带有重定向到文件的后台命令。

huangapple go评论89阅读模式
英文:

Start detached command with redirect to file

问题

我正在尝试启动一个分离的进程,以便在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。

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

英文:

I'm trying to start a command in a detached process so that it can continue after go program exits. I need to redirect the output of the command to a file.

What I need is something like this:

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

Obviously such redirect doesn't work. As I immediately exit from the program after starting the long running command, I cannot open a file and bind it to the Stdout.

Is there any way to achieve such a redirect?

答案1

得分: 5

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

示例:

cmd := exec.Command("sh", "-c", "/tmp/test.sh > /tmp/out")
if err := cmd.Start(); err != nil {
    panic(err)
}
fmt.Println("进程 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 命令查看输出文件的内容。

请注意,您可以使用其他 shell 来执行您喜欢的脚本(以及 test.sh 脚本所指定的内容)。

例如,要使用 bash

cmd := exec.Command("/bin/bash", "-c", "/tmp/test.sh > /tmp/out")
// 其余部分保持不变

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

英文:

You may start a shell which executes your command / app, and you may redirect its output to a file. The shell will continue to run and execute your script / app even if your Go app exits.

Example:

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

Test it with this simple Go app (replace /tmp/test.sh with the name of the executable binary you compile this into):

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

This app simply prints a line to the standard output once every second. You can see how the output file is being written e.g. with tail -f /tmp/out.

Note that you may use other shells to execute your scripts to your liking (and to what the test.sh script dictates).

For example to use bash:

cmd := exec.Command(&quot;/bin/bash&quot;, &quot;-c&quot;, &quot;/tmp/test.sh &gt; /tmp/out&quot;)
// rest is unchanged

Note that the command to be executed by the shell is passed as a single string argument, and it is not broken down into multiple as you would do it if you were to execute it directly in the command prompt.

答案2

得分: 4

也许你可以尝试使用这个链接中的方法: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("打开文件时出错:%v", err)
    }
    defer f.Close()
    // 在这一行将输出重定向到文件
    command.Stdout = f
    if err := command.Start(); err != nil {
        fmt.Fprintln(os.Stderr, "命令执行失败:", err)
        os.Exit(1)
    }

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

不确定这是否适用于你的情况。我在本地尝试过,似乎可以正常工作...请记住,你的用户应该有权限创建/更新文件。

英文:

Maybe you can try to use this: https://stackoverflow.com/a/28918814/2728768

Opening a file (and os.File implements io.Writer), and then passing it as the command.Stdout could do the trick:

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

	fmt.Println(&quot;Process ID:&quot;, command.Process.Pid)
}

Not sure this could be a viable solution for your case. I've tried it locally and it seems working... remember that your user should be able to create/update the file.

huangapple
  • 本文由 发表于 2016年10月25日 22:53:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/40243197.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定