HOWTO · Go

How to Execute Shell Command in Go

Learn how to run external programs from Go with os/exec, capture output, handle errors, pass arguments, and set a command working directory. Shell features require explicitly invoking a shell.

On this page

To run an external program from Go, use the standard-library os/exec package. It starts the named executable directly; it does not implicitly run a shell. Shell features such as pipes, redirection, variable expansion, and wildcards are therefore not applied. If a task needs those features, invoke a shell explicitly and pass its script as an argument, taking care not to put untrusted text into that script.

Run a Command with os/exec

Create a command with exec.Command, then call a method such as Output to start it and collect its standard output. The first argument names the executable, and the remaining arguments are passed to that program. This complete program starts itself as a child process with one argument. The child prints a fixed line, and the parent captures that standard output and writes it to its own standard output. The program prints the child’s line through the parent process.

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

Here, os.Args[0] identifies the current executable, so the example does not depend on a separate utility being installed. In an application, replace it with the executable you need, such as git or go, and supply that program’s arguments separately.

Capture Output and Handle Errors

Check the error returned by Output or CombinedOutput before using the result. A failure to start a program, such as an executable that cannot be found, differs from a program that starts and exits with a nonzero status. In the latter case, the error is usually an *exec.ExitError; inspect it when the exit status matters. The Cmd type also lets you connect streams directly: set cmd.Stdout or cmd.Stderr to a writer when you want output sent there instead of collected in a byte slice.

Use Output when standard output is the data your program needs. Use CombinedOutput when you also need standard error in the returned bytes; the two streams are combined, so their relative ordering is not a reliable way to reconstruct separate streams. When diagnostics need to remain distinct, assign separate writers to cmd.Stdout and cmd.Stderr. Handle and report errors at the point where the caller can decide whether to retry, return, or continue; printing an error and then treating the command as successful can hide a failed operation.

Pass Arguments to a Command

Pass each argument as its own string after the executable name, for example exec.Command("grep", "-n", pattern, filename). Go does not join these values into a shell command or interpret spaces, quotes, pipes, or redirection characters for you. A filename containing spaces remains one argument when supplied as one string. This avoids shell parsing, but it does not validate input for the invoked program: an argument may still have special meaning to that program, so validate values according to the command’s own interface.

For input and output streams, assign an io.Reader to cmd.Stdin and writers to cmd.Stdout or cmd.Stderr, then call Run. The example executable above uses Output because it only needs to capture a short result. Use Run when you want to connect streams and do not need to collect standard output as a return value. The executable must be available to the process, and its options can differ across operating systems.

Run a Command in a Different Directory

Set cmd.Dir before starting the command to choose its working directory. For example, after constructing a command, set cmd.Dir = projectDir and then call Output, CombinedOutput, or Run. The directory must exist and be accessible to the process. A relative cmd.Dir is resolved relative to the Go process’s current working directory; use an absolute path when the caller’s starting directory may vary. This setting changes the child process’s directory and does not change the Go process’s own working directory.