Go podle Příkladů: Vytváření podprocesů

Někdy naše Go programy potřebují spustit jiné procesy.

package main
import (
    "errors"
    "fmt"
    "io"
    "os/exec"
)
func main() {

Začneme jednoduchým příkazem který nemá žádné argumenty nebo vstup a prostě vypisuje něco na stdout. Pomocný příkaz exec.Command vytváří objekt pro reprezentaci tohoto externího procesu.

    dateCmd := exec.Command("date")

Metoda Output spustí příkaz, počká na jeho ukončení a posbírá jeho standardní výstup. Pokud nedošlo k žádným chybám, bude dateOut obsahovat bajty s informací o datu.

    dateOut, err := dateCmd.Output()
    if err != nil {
        panic(err)
    }
    fmt.Println("> date")
    fmt.Println(string(dateOut))

Output a další metody Command vrátí *exec.Error, pokud byl při spouštění příkazu problém (např. špatná cesta), a *exec.ExitError, pokud se příkaz spustil, ale skončil s nenulovým návratovým kódem.

    _, err = exec.Command("date", "-x").Output()
    if err != nil {
        if e, ok := errors.AsType[*exec.Error](err); ok {
            fmt.Println("spuštění selhalo:", e)
        } else if e, ok := errors.AsType[*exec.ExitError](err); ok {
            exitCode := e.ExitCode()
            fmt.Println("návratový kód příkazu =", exitCode)
        } else {
            panic(err)
        }
    }

Dál se podíváme na trochu komplikovanější případ kde propojíme pomocí pipe data do externího procesu na jeho stdin a posbíráme výsledky z jeho stdout.

    grepCmd := exec.Command("grep", "hello")

Tady explicitně vezmeme vstupní a výstupní pipe, nastartujeme proces, zapíšeme do něho nějaký vstup, načteme výsledný výstup, a na závěr počkáme na ukončení tohoto procesu.

    grepIn, _ := grepCmd.StdinPipe()
    grepOut, _ := grepCmd.StdoutPipe()
    grepCmd.Start()
    grepIn.Write([]byte("hello grep\ngoodbye grep"))
    grepIn.Close()
    grepBytes, _ := io.ReadAll(grepOut)
    grepCmd.Wait()

V našem příkladu výše jsme vynechali kontroly chyb, ale pro všechny můžete použít obvyklý vzor if err != nil. Také sbíráme výsledky jen ze StdoutPipe, ale mohli byste přesně stejným způsobem sbírat i StderrPipe.

    fmt.Println("> grep hello")
    fmt.Println(string(grepBytes))

Všimněte si, že když spouštíme příkaz musíme poskytnout explicitně definovaný příkaz a pole argumentů v porovnání s prostým předáním jednoho řetězce s příkazovou řádkou. Pokud chcete spustit plný příkaz pomocí řetězce, můžete použít volbu -c bash shellu:

    lsCmd := exec.Command("bash", "-c", "ls -a -l -h")
    lsOut, err := lsCmd.Output()
    if err != nil {
        panic(err)
    }
    fmt.Println("> ls -a -l -h")
    fmt.Println(string(lsOut))
}

Spuštěné programy vrací výstup který je stejný jako bychom je spustili přímo z příkazové řádky.

$ go run spawning-processes.go 
> date
Thu 05 May 2022 10:10:12 PM PDT

date nemá volbu -x, takže skončí s chybovou hláškou a nenulovým návratovým kódem.

návratový kód příkazu = 1
> grep hello
hello grep
> ls -a -l -h
drwxr-xr-x  4 mark 136B Oct 3 16:29 .
drwxr-xr-x 91 mark 3.0K Oct 3 12:50 ..
-rw-r--r--  1 mark 1.3K Oct 3 16:28 spawning-processes.go

Další příklad: Spouštění procesů.