英文:
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("ls" ,"/no/such/file")
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's answer.
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论