Capturing main process stdout into bash variable, keep printing subprocess output to console?

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

Capturing main process stdout into bash variable, keep printing subprocess output to console?

问题

我正在开发一个命令行界面(CLI)。CLI会将输出写入stdout,我希望能够将其捕获到一个bash变量中。也就是说,我希望能够执行以下操作:

output=$(my_cli_command)

我的CLI还会在该命令中运行一个子进程。当执行output=$(my_cli_command)时,我希望子进程的输出能够打印到终端上。

在运行子进程时,我需要做些什么来确保子进程的输出(1)打印到控制台上,但是(2)不被捕获到output变量中?或者这是否不可能实现?

我正在使用Go语言编写我的CLI。我在这里提供了一个可复现的示例:

package main

import (
	"fmt"
	"os"
	"os/exec"
)

func main() {
	fmt.Println("我希望这个被捕获")

	// 我不希望这个被捕获,但是我希望"ls"的输出打印到控制台上
	command := exec.Command("ls")
	// 使用这些代码,打印语句和ls语句都会打印到控制台上。
	// 所有的输出都会被捕获到一个bash变量中。
	command.Stdout = os.Stdout
	command.Stderr = os.Stderr
	
	command.Run()

}

将其复制到一个名为main.go的文件中,并像这样运行它:

output=$(go run main.go)
echo "$output"

谢谢。

英文:

I'm developing a CLI. The CLI writes to stdout that I'd like to capture in a bash variable. That is, I'd like to be able to do this:

output=$(my_cli_command)

My CLI also runs a subprocess in that command. I'd like the output of the subprocess to be printed to the terminal when output=$(my_cli_command) is executed.

What do I have to do when running the subprocess to ensure that it (1) is printed to the console but (2) is not captured into the output variable? Or is this not possible?

I'm writing my CLI in Go. I've put a reproducible example here:

package main

import (
	"fmt"
	"os"
	"os/exec"
)

func main() {
	fmt.Println("I want this captured")

	// I don't want this captured, but I want "ls" printed to console
	command := exec.Command("ls")
	// With these lines, the print statement and the ls statement are printed to console.
	// All are captured in a bash variable.
	command.Stdout = os.Stdout
	command.Stderr = os.Stderr
	
	command.Run()

}

Copy that into a file main.go and run it like this:

output=$(go run main.go)
echo "$output"

Thank you.

答案1

得分: 3

这不是一个bash的问题,而是一个Go的问题。如果你不想捕获ls命令的输出,可以将其标准输出连接到你的Go程序的标准错误输出,而不是标准输出。

package main

import (
    "fmt"
    "os"
    "os/exec"
)

func main() {
    fmt.Println("我希望这个被捕获")

    command := exec.Command("ls")
    command.Stdout = os.Stderr // 不是 os.Stdout
    command.Stderr = os.Stderr
    command.Run()
}
英文:

This isn't a bash problem, it's a Go problem. If you don't want the output of ls to be captured, connect its stdout to your Go program's stderr rather than its stdout.

package main

import (
    "fmt"
    "os"
    "os/exec"
)

func main() {
    fmt.Println("I want this captured")

    command := exec.Command("ls")
    command.Stdout = os.Stderr // not os.Stdout
    command.Stderr = os.Stderr
    command.Run()
}


</details>



# 答案2
**得分**: 1

清楚了`ls` 命令的输出被捕获是因为它被发送到标准输出(stdout)如果你运行的命令发送了任何内容到标准错误(stderr)它将会在终端上显示而不会被捕获就像你所希望的一样例如如果你将 Go 代码修改为以下形式

    command := exec.Command("ls", "/no/such/file")

你将会得到以下结果

     $ output=$(go run main.go)
     ls: /no/such/file: No such file or directory
     $ echo $output
     I want this captured

对于你的情况解决方案是将子命令的标准输出连接到 `os.Stderr` 而不是 `os.Stdout`就像 Charles Duffy 的回答中所示

<details>
<summary>英文:</summary>

To be clear, the `ls` output is captured _because it goes to stdout_. If you were to run a command that sent anything to stderr, it would be displayed on the terminal and not captured, just as you indicated you want. For example, if you change the Go code to do this:

    command := exec.Command(&quot;ls&quot; ,&quot;/no/such/file&quot;)

You get this result:

     $ output=$(go run main.go)
     ls: /no/such/file: No such file or directory
     $ echo $output
     I want this captured

The solution for your case is to connect the standard output of the child command to `os.Stderr` instead of `os.Stdout`, as in Charles Duffy&#39;s answer.

    



</details>



huangapple
  • 本文由 发表于 2023年5月25日 06:30:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/76327784.html
匿名

发表评论

匿名网友

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

确定