web-dev-qa-db-fra.com

Comment diriger plusieurs commandes dans Go?

Comment puis-je diriger plusieurs commandes externes ensemble dans Go? J'ai essayé ce code mais j'ai une erreur qui dit 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)
}
52
user1243746

StdoutPipe renvoie un canal qui sera connecté à la commande sortie standard au démarrage de la commande. Le tuyau sera fermé automatiquement après que Wait voit la commande quitter.

(de http://golang.org/pkg/os/exec/#Cmd.StdinPipe )

Le fait que vous faites c1.Wait ferme la stdoutPipe.

J'ai fait un exemple de travail (juste une démo, ajoutez l'erreur de capture!):

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)
}
37
Denys Séguret

Pour des scénarios simples, vous pouvez utiliser cette approche:

bash -c "echo 'your command goes here'"

Par exemple, cette fonction récupère le nom du modèle de la CPU à l'aide de commandes piped:

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)
}
77
Stefan Saru
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()
}
43
Matt

Comme la première réponse mais avec la première commande démarrée et attendue dans un goroutine. Cela garde la pipe heureuse.

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()
}
4
WeakPointer

Ceci est un exemple pleinement fonctionnel. La fonction Execute prend un nombre illimité d'instances exec.Cmd (à l'aide de la fonction variadic ), puis les parcourt en connectant correctement la sortie de stdout au stdin de la commande suivante. Cela doit être fait avant qu'une fonction ne soit appelée.

La fonction call appelle ensuite les commandes dans une boucle, en utilisant les différés pour appeler de manière récursive et en assurant la fermeture correcte des canaux. 

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

Disponible dans ce Gist

https://Gist.github.com/tyndyll/89fbb2c2273f83a074dc

Un bon point à savoir est que les variables Shell comme ~ ne sont pas interpolées

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

J'ai passé une bonne journée à essayer d’utiliser Denys Séguret answer pour créer un wrapper pour plusieurs exec.Command avant de tomber sur ce paquetage soigné by b4b4r07 .

0
eriel marimon