英文:
How to pipe several commands in Go?
问题
我如何在Go中将多个外部命令连接在一起?我尝试了这段代码,但是出现了一个错误,显示exit status 1
。
package main
import (
"io"
"log"
"os"
"os/exec"
)
func main() {
c1 := exec.Command("ls")
stdout1, err := c1.StdoutPipe()
if err != nil {
log.Fatal(err)
}
if err = c1.Start(); err != nil {
log.Fatal(err)
}
if err = c1.Wait(); err != nil {
log.Fatal(err)
}
c2 := exec.Command("wc", "-l")
c2.Stdin = stdout1
stdout2, err := c2.StdoutPipe()
if err != nil {
log.Fatal(err)
}
if err = c2.Start(); err != nil {
log.Fatal(err)
}
if err = c2.Wait(); err != nil {
log.Fatal(err)
}
io.Copy(os.Stdout, stdout2)
}
英文:
How can I pipe several external commands together in Go? I've tried this code but I get an error that says exit status 1
.
package main
import (
"io"
"log"
"os"
"os/exec"
)
func main() {
c1 := exec.Command("ls")
stdout1, err := c1.StdoutPipe()
if err != nil {
log.Fatal(err)
}
if err = c1.Start(); err != nil {
log.Fatal(err)
}
if err = c1.Wait(); err != nil {
log.Fatal(err)
}
c2 := exec.Command("wc", "-l")
c2.Stdin = stdout1
stdout2, err := c2.StdoutPipe()
if err != nil {
log.Fatal(err)
}
if err = c2.Start(); err != nil {
log.Fatal(err)
}
if err = c2.Wait(); err != nil {
log.Fatal(err)
}
io.Copy(os.Stdout, stdout2)
}
答案1
得分: 164
对于简单的场景,您可以使用以下方法:
bash -c "echo 'your command goes here'"
例如,这个函数使用管道命令获取CPU型号名称:
func getCPUmodel() string {
cmd := "cat /proc/cpuinfo | egrep '^model name' | uniq | awk '{print substr($0, index($0,$4))}'"
out, err := exec.Command("bash","-c",cmd).Output()
if err != nil {
return fmt.Sprintf("Failed to execute command: %s", cmd)
}
return string(out)
}
英文:
For simple scenarios, you could use this approach:
bash -c "echo 'your command goes here'"
For example, this function retrieves the CPU model name using piped commands:
func getCPUmodel() string {
cmd := "cat /proc/cpuinfo | egrep '^model name' | uniq | awk '{print substr($0, index($0,$4))}'"
out, err := exec.Command("bash","-c",cmd).Output()
if err != nil {
return fmt.Sprintf("Failed to execute command: %s", cmd)
}
return string(out)
}
答案2
得分: 69
> StdoutPipe返回一个管道,当命令开始时,该管道将连接到命令的标准输出。在Wait看到命令退出后,管道将自动关闭。
(from http://golang.org/pkg/os/exec/#Cmd.StdinPipe )
事实上,你执行c1.Wait
会关闭stdoutPipe
。
我做了一个工作示例(只是一个演示,添加错误捕获!):
package main
import (
"bytes"
"io"
"os"
"os/exec"
)
func main() {
c1 := exec.Command("ls")
c2 := exec.Command("wc", "-l")
r, w := io.Pipe()
c1.Stdout = w
c2.Stdin = r
var b2 bytes.Buffer
c2.Stdout = &b2
c1.Start()
c2.Start()
c1.Wait()
w.Close()
c2.Wait()
io.Copy(os.Stdout, &b2)
}
英文:
> StdoutPipe returns a pipe that will be connected to the command's
> standard output when the command starts. The pipe will be closed
> automatically after Wait sees the command exit.
(from http://golang.org/pkg/os/exec/#Cmd.StdinPipe )
The fact you do c1.Wait
closes the stdoutPipe
.
I made a working example (just a demo, add error catching!) :
package main
import (
"bytes"
"io"
"os"
"os/exec"
)
func main() {
c1 := exec.Command("ls")
c2 := exec.Command("wc", "-l")
r, w := io.Pipe()
c1.Stdout = w
c2.Stdin = r
var b2 bytes.Buffer
c2.Stdout = &b2
c1.Start()
c2.Start()
c1.Wait()
w.Close()
c2.Wait()
io.Copy(os.Stdout, &b2)
}
答案3
得分: 61
package main
import (
"os"
"os/exec"
)
func main() {
c1 := exec.Command("ls")
c2 := exec.Command("wc", "-l")
c2.Stdin, _ = c1.StdoutPipe()
c2.Stdout = os.Stdout
_ = c2.Start()
_ = c1.Run()
_ = c2.Wait()
}
英文:
package main
import (
"os"
"os/exec"
)
func main() {
c1 := exec.Command("ls")
c2 := exec.Command("wc", "-l")
c2.Stdin, _ = c1.StdoutPipe()
c2.Stdout = os.Stdout
_ = c2.Start()
_ = c1.Run()
_ = c2.Wait()
}
答案4
得分: 9
package main
import (
"io"
"os"
"os/exec"
)
func main() {
c1 := exec.Command("ls")
c2 := exec.Command("wc", "-l")
pr, pw := io.Pipe()
c1.Stdout = pw
c2.Stdin = pr
c2.Stdout = os.Stdout
c1.Start()
c2.Start()
go func() {
defer pw.Close()
c1.Wait()
}()
c2.Wait()
}
英文:
Like the first answer but with the first command started and waited for in a goroutine. This keeps the pipe happy.
package main
import (
"io"
"os"
"os/exec"
)
func main() {
c1 := exec.Command("ls")
c2 := exec.Command("wc", "-l")
pr, pw := io.Pipe()
c1.Stdout = pw
c2.Stdin = pr
c2.Stdout = os.Stdout
c1.Start()
c2.Start()
go func() {
defer pw.Close()
c1.Wait()
}()
c2.Wait()
}
答案5
得分: 7
这是一个完全可工作的示例。Execute
函数接受任意数量的exec.Cmd
实例(使用可变参数函数),然后循环正确地将stdout的输出附加到下一个命令的stdin上。这必须在调用任何函数之前完成。
然后,call
函数通过循环调用命令,使用延迟调用递归,并确保正确关闭管道。
package main
import (
"bytes"
"io"
"log"
"os"
"os/exec"
)
func Execute(output_buffer *bytes.Buffer, stack ...*exec.Cmd) (err error) {
var error_buffer bytes.Buffer
pipe_stack := make([]*io.PipeWriter, len(stack)-1)
i := 0
for ; i < len(stack)-1; i++ {
stdin_pipe, stdout_pipe := io.Pipe()
stack[i].Stdout = stdout_pipe
stack[i].Stderr = &error_buffer
stack[i+1].Stdin = stdin_pipe
pipe_stack[i] = stdout_pipe
}
stack[i].Stdout = output_buffer
stack[i].Stderr = &error_buffer
if err := call(stack, pipe_stack); err != nil {
log.Fatalln(string(error_buffer.Bytes()), err)
}
return err
}
func call(stack []*exec.Cmd, pipes []*io.PipeWriter) (err error) {
if stack[0].Process == nil {
if err = stack[0].Start(); err != nil {
return err
}
}
if len(stack) > 1 {
if err = stack[1].Start(); err != nil {
return err
}
defer func() {
if err == nil {
pipes[0].Close()
err = call(stack[1:], pipes[1:])
}
}()
}
return stack[0].Wait()
}
func main() {
var b bytes.Buffer
if err := Execute(&b,
exec.Command("ls", "/Users/tyndyll/Downloads"),
exec.Command("grep", "as"),
exec.Command("sort", "-r"),
); err != nil {
log.Fatalln(err)
}
io.Copy(os.Stdout, &b)
}
在此gist中可用。
需要注意的一个重要点是,像~这样的shell变量不会被插值。
英文:
This is a fully working example. The Execute
function takes any number of exec.Cmd
instances (using a variadic function) and then loops over them correctly attaching the output of stdout to the stdin of the next command. This must be done before any function is called.
The call function then goes about calling the commands in a loop, using defers to call recursively and ensuring proper closure of pipes
package main
import (
"bytes"
"io"
"log"
"os"
"os/exec"
)
func Execute(output_buffer *bytes.Buffer, stack ...*exec.Cmd) (err error) {
var error_buffer bytes.Buffer
pipe_stack := make([]*io.PipeWriter, len(stack)-1)
i := 0
for ; i < len(stack)-1; i++ {
stdin_pipe, stdout_pipe := io.Pipe()
stack[i].Stdout = stdout_pipe
stack[i].Stderr = &error_buffer
stack[i+1].Stdin = stdin_pipe
pipe_stack[i] = stdout_pipe
}
stack[i].Stdout = output_buffer
stack[i].Stderr = &error_buffer
if err := call(stack, pipe_stack); err != nil {
log.Fatalln(string(error_buffer.Bytes()), err)
}
return err
}
func call(stack []*exec.Cmd, pipes []*io.PipeWriter) (err error) {
if stack[0].Process == nil {
if err = stack[0].Start(); err != nil {
return err
}
}
if len(stack) > 1 {
if err = stack[1].Start(); err != nil {
return err
}
defer func() {
if err == nil {
pipes[0].Close()
err = call(stack[1:], pipes[1:])
}
}()
}
return stack[0].Wait()
}
func main() {
var b bytes.Buffer
if err := Execute(&b,
exec.Command("ls", "/Users/tyndyll/Downloads"),
exec.Command("grep", "as"),
exec.Command("sort", "-r"),
); err != nil {
log.Fatalln(err)
}
io.Copy(os.Stdout, &b)
}
Available in this gist
https://gist.github.com/tyndyll/89fbb2c2273f83a074dc
A good point to know is that shell variables like ~ are not interpolated
答案6
得分: 3
package main
import (
...
pipe "github.com/b4b4r07/go-pipe"
)
func main() {
var b bytes.Buffer
pipe.Command(&b,
exec.Command("ls", "/Users/b4b4r07/Downloads"),
exec.Command("grep", "Vim"),
)
io.Copy(os.Stdout, &b)
}
英文:
package main
import (
...
pipe "github.com/b4b4r07/go-pipe"
)
func main() {
var b bytes.Buffer
pipe.Command(&b,
exec.Command("ls", "/Users/b4b4r07/Downloads"),
exec.Command("grep", "Vim"),
)
io.Copy(os.Stdout, &b)
}
I spent a good day trying to use Denys Séguret answer to come up with a wrapper for multiple exec.Command
before I came across this neat package by b4b4r07.
答案7
得分: 2
package main
import (
"io"
"os/exec"
)
func main() {
ffmpeg := exec.Command(
"ffmpeg", "-i", "247.webm", "-i", "251.webm", "-c", "copy", "-f", "webm", "-",
)
ffplay := exec.Command("ffplay", "-")
ffplay.Stdin, ffmpeg.Stdout = io.Pipe()
ffmpeg.Start()
ffplay.Run()
}
英文:
I wanted to pipe some video and audio to FFplay. This worked for me:
package main
import (
"io"
"os/exec"
)
func main() {
ffmpeg := exec.Command(
"ffmpeg", "-i", "247.webm", "-i", "251.webm", "-c", "copy", "-f", "webm", "-",
)
ffplay := exec.Command("ffplay", "-")
ffplay.Stdin, ffmpeg.Stdout = io.Pipe()
ffmpeg.Start()
ffplay.Run()
}
答案8
得分: 1
因为构建这样的命令链可能会很复杂,所以我决定实现一个小型的Go库来实现这个目的:https://github.com/rainu/go-command-chain
package main
import (
"bytes"
"fmt"
"github.com/rainu/go-command-chain"
)
func main() {
output := &bytes.Buffer{}
err := cmdchain.Builder().
Join("ls").
Join("wc", "-l").
Finalize().WithOutput(output).Run()
if err != nil {
panic(err)
}
fmt.Printf("Errors found: %s", output)
}
借助这个库的帮助,您还可以配置标准错误转发和其他功能。
英文:
Because it can be complex to build such command chains I have decided to implements a litte go library for that purpose: https://github.com/rainu/go-command-chain
package main
import (
"bytes"
"fmt"
"github.com/rainu/go-command-chain"
)
func main() {
output := &bytes.Buffer{}
err := cmdchain.Builder().
Join("ls").
Join("wc", "-l").
Finalize().WithOutput(output).Run()
if err != nil {
panic(err)
}
fmt.Printf("Errors found: %s", output)
}
With the help of this lib you can also configure std-error forwarding and other things.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论