Spawning Processes

Spawning Processes #

// Por vezes é necessário que programas em
// Go invoquem processos ou programas _não-Go_.

package main

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

func main() {

	// De início será utilizado um simples comando que não recebe
	// nenhum argumento ou input e apenas imprime algo no `stdout`.
	// O comando `exec.Command` cria um objeto para representar
	// este processo externo.
	dateCmd := exec.Command("date")

	// O método `Output` executa o comando, aguarda sua finalização
	// e coleta o output padrão.
	// Se não houver nenhum erro, `dateOut` terá os bytes com informação
	// da data.
	dateOut, err := dateCmd.Output()
	if err != nil {
		panic(err)
	}
	fmt.Println("> date")
	fmt.Println(string(dateOut))

	// `Output` e outros métodos de `Command` retornarão
	// um `*exec.Error` se houver algum problema ao executar
	// o comando, como por exemplo caminho errado, e
	// `*exec.ExitError` se o comando foi executado mas
	// retornou com um código não-zero.
	_, err = exec.Command("date", "-x").Output()
	if err != nil {
		switch e := err.(type) {
		case *exec.Error:
			fmt.Println("failed executing:", err)
		case *exec.ExitError:
			fmt.Println("command exit rc =", e.ExitCode())
		default:
			panic(err)
		}
	}

	// Agora será apresentado um caso um pouco mais complexo
	// em que é enviado um dado para o processo externo em seu
	// `stdin`, através de um pipe e coletado o resultado a
	// partir de seu `stdout` também a partir de um pipe.
	grepCmd := exec.Command("grep", "hello")

	// Aqui se instancia os pipes, inicializa o próprio
	// processo, então escreve e envia algum input, lê o
	// resultado de saída e, finalmente, aguarda o processo
	// finalizar.
	grepIn, _ := grepCmd.StdinPipe()
	grepOut, _ := grepCmd.StdoutPipe()
	grepCmd.Start()
	grepIn.Write([]byte("hello grep\ngoodbye grep"))
	grepIn.Close()
	grepBytes, _ := io.ReadAll(grepOut)
	grepCmd.Wait()

	// Verificação de erros foram omitidas no exemplo acima,
	// mas é possível utilizar o padrão usual `if err != nil`
	// para todos eles. Também foi coletado apenas o resultado
	// de `StdoutPipe`, mas é possível também coletar de
	// `StderrPipe` exatamente do mesmo jeito.
	fmt.Println("> grep hello")
	fmt.Println(string(grepBytes))

	// Note que, ao invocar comandos, é necessário fornecer
	// um comando explicitamente delineado e um array de argumentos
	// ao invés de passar apenas uma string de linha de comando
	// command-line string. Se a intenção for gerar um comando
	// com uma string, é possível utilizar a flag `-c` do `bash`.
	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))
}
# Os processos invocados retornam o mesmo output que  
# retornaria se fosse executado diretamente da
# linha de comando.
$ go run spawning-processes.go 
> date
Thu 05 May 2022 10:10:12 PM PDT

# `date` não possui uma flag `-x`, então será finalizado
# com mensagem de erro e código de retorno não-zero.
command exited with rc = 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