95 votes

Obtenir le code de sortie - Go

J'utilise le paquet : os/exec http://golang.org/pkg/os/exec/ pour exécuter une commande dans le système d'exploitation, mais je n'arrive pas à trouver le moyen d'obtenir le code de sortie. Je peux cependant lire la sortie

ie.

package main

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

func main() {
    cmd := exec.Command("somecommand", "parameter")
    var out bytes.Buffer
    cmd.Stdout = &out
    if err := cmd.Run() ; err != nil {
        //log.Fatal( cmd.ProcessState.Success() )
        log.Fatal( err )
    }
    fmt.Printf("%q\n", out.String() )
}

112voto

tux21b Points 17336

Il est facile de déterminer si le code de sortie était 0 ou autre chose. Dans le premier cas, cmd.Wait() renverra zéro (à moins qu'il n'y ait une autre erreur lors de la mise en place des tuyaux).

Malheureusement, il n'existe pas de moyen indépendant de la plate-forme pour obtenir le code de sortie en cas d'erreur. C'est aussi la raison pour laquelle cela ne fait pas partie de l'API. L'extrait suivant fonctionnera sous Linux, mais je ne l'ai pas testé sur d'autres plateformes :

package main

import "os/exec"
import "log"
import "syscall"

func main() {
    cmd := exec.Command("git", "blub")

    if err := cmd.Start(); err != nil {
        log.Fatalf("cmd.Start: %v", err)
    }

    if err := cmd.Wait(); err != nil {
        if exiterr, ok := err.(*exec.ExitError); ok {
            // The program has exited with an exit code != 0

            // This works on both Unix and Windows. Although package
            // syscall is generally platform dependent, WaitStatus is
            // defined for both Unix and Windows and in both cases has
            // an ExitStatus() method with the same signature.
            if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
                log.Printf("Exit Status: %d", status.ExitStatus())
            }
        } else {
            log.Fatalf("cmd.Wait: %v", err)
        }
    }
}

Juste suivre les api documents pour en savoir plus :)

96voto

David Points 3254

Depuis la version 1.12 de golang, le code de sortie est disponible nativement et de manière multiplateforme. Voir ExitError y ExitCode() .

ExitCode renvoie le code de sortie du processus quitté, ou -1 si le processus n'a pas été quitté ou s'il a été interrompu par un signal.

if err := cmd.Run() ; err != nil {
    if exitError, ok := err.(*exec.ExitError); ok {
        return exitError.ExitCode()
    }
}

27voto

Reorx Points 586

Voici ma version améliorée basée sur la réponse de @tux21b

utils/cmd.go

package utils

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

const defaultFailedCode = 1

func RunCommand(name string, args ...string) (stdout string, stderr string, exitCode int) {
    log.Println("run command:", name, args)
    var outbuf, errbuf bytes.Buffer
    cmd := exec.Command(name, args...)
    cmd.Stdout = &outbuf
    cmd.Stderr = &errbuf

    err := cmd.Run()
    stdout = outbuf.String()
    stderr = errbuf.String()

    if err != nil {
        // try to get the exit code
        if exitError, ok := err.(*exec.ExitError); ok {
            ws := exitError.Sys().(syscall.WaitStatus)
            exitCode = ws.ExitStatus()
        } else {
            // This will happen (in OSX) if `name` is not available in $PATH,
            // in this situation, exit code could not be get, and stderr will be
            // empty string very likely, so we use the default fail code, and format err
            // to string and set to stderr
            log.Printf("Could not get exit code for failed program: %v, %v", name, args)
            exitCode = defaultFailedCode
            if stderr == "" {
                stderr = err.Error()
            }
        }
    } else {
        // success, exitCode should be 0 if go is ok
        ws := cmd.ProcessState.Sys().(syscall.WaitStatus)
        exitCode = ws.ExitStatus()
    }
    log.Printf("command result, stdout: %v, stderr: %v, exitCode: %v", stdout, stderr, exitCode)
    return
}

Je l'ai testé sur OSX, s'il ne fonctionne pas comme prévu sur d'autres plateformes, dites-le moi pour que nous puissions l'améliorer.

16voto

colminator Points 2707

Septembre 2019, Go 1.13 introduite errors.As qui prend en charge le "déballage" des erreurs - ce qui est pratique pour trouver des erreurs précises dans une chaîne d'appels imbriquée.

Il s'agit donc d'extraire et d'inspecter les deux erreurs les plus courantes lors de l'exécution d'une commande externe :


err := cmd.Run()

var (
    ee *exec.ExitError
    pe *os.PathError
)

if errors.As(err, &ee) {
    log.Println("exit code error:", ee.ExitCode()) // ran, but non-zero exit code

} else if errors.As(err, &pe) {
    log.Printf("os.PathError: %v", pe) // "no such file ...", "permission denied" etc.

} else if err != nil {
    log.Printf("general error: %v", err) // something really bad happened!

} else {
    log.Println("success!") // ran without error (exit code zero)
}

1voto

John Siu Points 4229

J'ai récemment rencontré ce problème lors du développement de mon paquet d'aide.

Basé sur l'exemple de code ici : https://go-review.googlesource.com/c/go/+/213337/1/src/os/exec/example_test.go

func ExampleExitError() {
    cmd := exec.Command("sleep", "-u")
    err := cmd.Run()
    var exerr *exec.ExitError
    if errors.As(err, &exerr) {
        fmt.Printf("the command exited unsuccessfully: %d\n", exerr.ExitCode())
}

Le code de sortie

J'ai fini par faire ce qui suit pour mon propre wrapper exec cmd :

// Return exit code
func (self *MyCmd) ExitCode() int {
    var exitErr *exec.ExitError
    if errors.As(self.Err, &exitErr) {
        return exitErr.ExitCode()
    }
    // No error
    return 0
}

self.Err est la valeur de retour d'un exec.Command.Run() . La liste complète est ici : https://github.com/J-Siu/go-helper/blob/master/myCmd.go

Message d'erreur textuel

Alors que la réponse de @colm.anseo tient compte de os.patherror il ne donne pas de code d'erreur (int), et IMHO devrait être traité séparément. A la place, un message d'erreur textuel peut être extrait de execCmd.Stderr aimer suivre :

func (self *MyCmd) Run() error {
    execCmd := exec.Command(self.CmdName, *self.ArgsP...)
    execCmd.Stdout = &self.Stdout
    execCmd.Stderr = &self.Stderr
    execCmd.Dir = self.WorkDir
    self.CmdLn = execCmd.String()
    self.Err = execCmd.Run()
    self.Ran = true
    ReportDebug(&self, "myCmd:", false, false)
    ReportDebug(self.Stderr.String(), "myCmd:Stderr", false, false)
    ReportDebug(self.Stdout.String(), "myCmd:Stdout", false, false)
    return self.Err
}

self.Stderr est un bytes.Buffer et passer en execCmd avant Run() . Après execCmd.Run() Le texte err peut être extrait à l'aide de self.Stderr.String() .

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