116 votes

Exécuter une commande shell en Go

Je cherche à exécuter une commande shell en Go et à obtenir le résultat sous forme de chaîne de caractères dans mon programme. J'ai vu la commande Code Rosetta version :

package main
import "fmt"
import "exec"

func main() {
  cmd, err := exec.Run("/bin/ls", []string{"/bin/ls"}, []string{}, "", exec.DevNull, exec.PassThrough, exec.PassThrough)
  if (err != nil) {
    fmt.Println(err)
    return
  }
  cmd.Close()

Mais cela ne capture pas le standard out ou err de manière à ce que je puisse y accéder de manière programmatique - ils s'affichent toujours dans les stdout / stderr normaux. J'ai vu que l'utilisation de Pipe en tant que out ou err pourrait aider ailleurs, mais il n'y a pas d'exemple de la façon de le faire. Des idées ?

240voto

Lourenco Points 1930

Le paquet "exec" était modifié un peu. Les éléments suivants code a fonctionné pour moi.

package main

import (
    "fmt"
    "os/exec"
)

func main() {
    app := "echo"

    arg0 := "-e"
    arg1 := "Hello world"
    arg2 := "\n\tfrom"
    arg3 := "golang"

    cmd := exec.Command(app, arg0, arg1, arg2, arg3)
    stdout, err := cmd.Output()

    if err != nil {
        fmt.Println(err.Error())
        return
    }

    // Print the output
    fmt.Println(string(stdout))
}

75voto

typetetris Points 2700

Aucune des réponses fournies ne permet de séparer stdout y stderr J'essaie donc une autre réponse.

Tout d'abord, vous obtiendrez toutes les informations dont vous avez besoin en consultant la documentation de l'application exec.Cmd dans le os/exec l'emballage. Voir ici : https://golang.org/pkg/os/exec/#Cmd

En particulier les membres Stdin y Stdout , Stderr où tout io.Reader peut être utilisé pour alimenter stdin de votre processus nouvellement créé et de tout io.Writer peut être utilisé pour consommer des stdout y stderr de votre commandement.

La fonction Shellout dans le programme suivant exécutera votre commande et vous donnera sa sortie et la sortie d'erreur séparément sous forme de chaînes de caractères.

Comme la valeur du paramètre est exécutée en tant que commande shell, il convient d'assainir toutes les entrées externes utilisées dans la construction de la valeur du paramètre.

Il est probable qu'il ne soit pas utilisé sous cette forme dans la production.

package main

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

const ShellToUse = "bash"

func Shellout(command string) (string, string, error) {
    var stdout bytes.Buffer
    var stderr bytes.Buffer
    cmd := exec.Command(ShellToUse, "-c", command)
    cmd.Stdout = &stdout
    cmd.Stderr = &stderr
    err := cmd.Run()
    return stdout.String(), stderr.String(), err
}

func main() {
    out, errout, err := Shellout("ls -ltr")
    if err != nil {
        log.Printf("error: %v\n", err)
    }
    fmt.Println("--- stdout ---")
    fmt.Println(out)
    fmt.Println("--- stderr ---")
    fmt.Println(errout)
}

12voto

jimt Points 7028

Cette réponse ne représente pas l'état actuel de la bibliothèque standard de Go. Veuillez consulter Réponse de @Lourenco pour une méthode actualisée !


Votre exemple ne lit pas réellement les données de stdout. Voici ce qui fonctionne pour moi.

package main

import (
   "fmt"
   "exec"
   "os"
   "bytes"
   "io"
)

func main() {
    app := "/bin/ls"
    cmd, err := exec.Run(app, []string{app, "-l"}, nil, "", exec.DevNull, exec.Pipe, exec.Pipe)

    if (err != nil) {
       fmt.Fprintln(os.Stderr, err.String())
       return
    }

    var b bytes.Buffer
    io.Copy(&b, cmd.Stdout)
    fmt.Println(b.String())

    cmd.Close()
}

7voto

qing Points 71
// Wrap exec, with option to use bash shell

func Cmd(cmd string, shell bool) []byte {

    if shell {
        out, err := exec.Command("bash", "-c", cmd).Output()
        if err != nil {
            panic("some error found")
        }
        return out
    } else {
        out, err := exec.Command(cmd).Output()
        if err != nil {
            panic("some error found")
        }
        return out
    }
}

vous pouvez essayer ceci .

3voto

Xeoncross Points 13263

Voici une fonction simple qui exécutera votre commande et capturera l'erreur, stdout et stderr pour que vous puissiez l'inspecter. Vous pouvez facilement voir tout ce qui pourrait mal se passer ou vous être rapporté.

// RunCMD is a simple wrapper around terminal commands
func RunCMD(path string, args []string, debug bool) (out string, err error) {

    cmd := exec.Command(path, args...)

    var b []byte
    b, err = cmd.CombinedOutput()
    out = string(b)

    if debug {
        fmt.Println(strings.Join(cmd.Args[:], " "))

        if err != nil {
            fmt.Println("RunCMD ERROR")
            fmt.Println(out)
        }
    }

    return
}

Vous pouvez l'utiliser comme suit (Conversion d'un fichier multimédia) :

args := []string{"-y", "-i", "movie.mp4", "movie_audio.mp3", "INVALID-ARG!"}
output, err := RunCMD("ffmpeg", args, true)

if err != nil {
    fmt.Println("Error:", output)
} else {
    fmt.Println("Result:", output)
}

Je l'ai utilisé avec Go 1.2-1.7

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X