Wie kann ich in Go mehrere externe Befehle zusammenleiten? Ich habe diesen Code ausprobiert, erhalte jedoch eine Fehlermeldung 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)
}
os.Pipe()
? weilio.Pipe()
IPC ohneos.Pipe()
vsio.Pipe()
.os.Pipe()
kehrt zurückFile*
und die Dokumentation sagt:Pipe returns a connected pair of Files; reads from r return bytes written to w. It returns the files and an error, if any.
Also, wie unterscheidet sich das vonio.Reader
&io.Writer
dasio.Pipe()
zurück?Für einfache Szenarien können Sie diesen Ansatz verwenden:
bash -c "echo 'your command goes here'"
Diese Funktion ruft beispielsweise den Namen des CPU-Modells mithilfe von Piped-Befehlen ab:
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) }
quelle
fork/exec /bin/bash: argument list too long
finden Sie hier . In diesem Fall müssen Sie möglicherweise Ihren Befehl ändern oder aufteilen oder auf die umfangreicherenio.Pipe
Methoden zurückgreifen, die an anderer Stelle in den Antworten dieser Frage aufgeführt sind.bash
mit einer Option (-c
) und einem Argument 'shell command'. Man könnte argumentieren, dass diesbash
möglicherweise nicht auf dem System verfügbar ist, und das ist viel wahrscheinlicher als ein 100-KB-Befehl, um diese Lösung zu brechen. Ein Bündel von Pipes und Puffern + Dutzend Codezeilen zum Sammeln einer einzeiligen Shell-Befehlsausgabe (die nicht einmal mehr als einzeilig gelesen wird) ist einfach inakzeptabel. Ich denke, das sollte akzeptiert werden.bash
. Das ist gut!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() }
quelle
Wie die erste Antwort, aber mit dem ersten Befehl gestartet und in einer Goroutine gewartet. Das macht die Pfeife glücklich.
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() }
quelle
os.Pipe()
... wennio.Pipe()
IPC durchgeführt werden kann? albertoleal.me/posts/golang-pipes.htmlDies ist ein voll funktionsfähiges Beispiel. Die
Execute
Funktion nimmt eine beliebige Anzahl vonexec.Cmd
Instanzen (unter Verwendung einer variadischen Funktion ) und durchläuft sie dann korrekt, wobei die Ausgabe von stdout korrekt an die stdin des nächsten Befehls angehängt wird . Dies muss erfolgen, bevor eine Funktion aufgerufen wird.Die Aufruffunktion ruft dann die Befehle in einer Schleife auf, verwendet Verzögerungen, um rekursiv aufzurufen, und stellt das ordnungsgemäße Schließen von Pipes sicher
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) }
Verfügbar in diesem Kern
https://gist.github.com/tyndyll/89fbb2c2273f83a074dc
Ein guter Punkt zu wissen ist, dass Shell-Variablen wie ~ nicht interpoliert werden
quelle
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) }
Ich habe einen guten Tag damit verbracht, die Antwort von Denys Séguret zu verwenden, um einen Wrapper für mehrere zu entwickeln,
exec.Command
bevor ich auf dieses nette Paket von b4b4r07 gestoßen bin .quelle