HOWTO · Go

Cómo ejecutar un comando de shell en Go

Aprenda a ejecutar programas externos desde Go con os/exec, capturar la salida, manejar errores, pasar argumentos y establecer un directorio de trabajo para un comando. Las características del shell requieren invocar un shell explícitamente.

En esta página

Para ejecutar un programa externo desde Go, utilice el paquete os/exec de la biblioteca estándar. Inicia el ejecutable nombrado directamente; no ejecuta implícitamente un shell. Por lo tanto, no se aplican características del shell como tuberías, redirección, expansión de variables y comodines. Si una tarea necesita esas características, invoque un shell explícitamente y pase su script como argumento, teniendo cuidado de no poner texto no confiable en ese script.

Ejecutar un comando con os/exec

Cree un comando con exec.Command y luego llame a un método como Output para iniciarlo y recoger su salida estándar. El primer argumento nombra el ejecutable, y los argumentos restantes se pasan a ese programa. Este programa completo se inicia a sí mismo como un proceso hijo con un argumento. El hijo imprime una línea fija, y el padre captura esa salida estándar y la escribe en su propia salida estándar. El programa imprime la línea del hijo a través del proceso padre.

package main

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

func main() {
	if len(os.Args) > 1 && os.Args[1] == "child" {
		fmt.Println("hello from child")
		return
	}

	cmd := exec.Command(os.Args[0], "child")
	output, err := cmd.Output()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	fmt.Print(string(output))
}
hello from child

Aquí, os.Args[0] identifica el ejecutable actual, por lo que el ejemplo no depende de que esté instalada una utilidad separada. En una aplicación, sustitúyalo por el ejecutable que necesite, como git o go, y proporcione los argumentos de ese programa por separado.

Capturar la salida y manejar errores

Compruebe el error devuelto por Output o CombinedOutput antes de utilizar el resultado. Un fallo al iniciar un programa, como un ejecutable que no se puede encontrar, difiere de un programa que se inicia y termina con un estado distinto de cero. En este último caso, el error suele ser un *exec.ExitError; inspecciónelo cuando el estado de salida sea importante. El tipo Cmd también le permite conectar flujos directamente: establezca cmd.Stdout o cmd.Stderr en un escritor cuando desee que la salida se envíe allí en lugar de recogerse en un segmento de bytes.

Utilice Output cuando la salida estándar sean los datos que su programa necesita. Utilice CombinedOutput cuando también necesite el error estándar en los bytes devueltos; los dos flujos se combinan, por lo que su orden relativo no es una forma fiable de reconstruir flujos separados. Cuando los diagnósticos deban permanecer distintos, asigne escritores separados a cmd.Stdout y cmd.Stderr. Maneje e informe de los errores en el punto en el que la persona que llama puede decidir si reintentar, volver o continuar; imprimir un error y luego tratar el comando como exitoso puede ocultar una operación fallida.

Pasar argumentos a un comando

Pase cada argumento como su propia cadena después del nombre del ejecutable, por ejemplo exec.Command("grep", "-n", pattern, filename). Go no une estos valores en un comando de shell ni interpreta espacios, comillas, tuberías o caracteres de redirección por usted. Un nombre de archivo que contiene espacios sigue siendo un argumento cuando se proporciona como una cadena. Esto evita el análisis del shell, pero no valida la entrada para el programa invocado: un argumento puede seguir teniendo un significado especial para ese programa, así que valide los valores según la propia interfaz del comando.

Para los flujos de entrada y salida, asigne un io.Reader a cmd.Stdin y escritores a cmd.Stdout o cmd.Stderr, y luego llame a Run. El ejecutable de ejemplo anterior utiliza Output porque solo necesita capturar un resultado corto. Utilice Run cuando desee conectar flujos y no necesite recoger la salida estándar como valor de retorno. El ejecutable debe estar disponible para el proceso, y sus opciones pueden diferir entre sistemas operativos.

Ejecutar un comando en un directorio diferente

Establezca cmd.Dir antes de iniciar el comando para elegir su directorio de trabajo. Por ejemplo, después de construir un comando, establezca cmd.Dir = projectDir y luego llame a Output, CombinedOutput o Run. El directorio debe existir y ser accesible para el proceso. Un cmd.Dir relativo se resuelve en relación con el directorio de trabajo actual del proceso Go; utilice una ruta absoluta cuando el directorio inicial de la persona que llama pueda variar. Esta configuración cambia el directorio del proceso hijo y no cambia el propio directorio de trabajo del proceso Go.