Executing external commands in Go


2014-08-17 · 2 min read

Use os/exec package to execute external commands in Go. To run the examples packages os, bytes, syscall, fmt, time must be imported.

output, err := exec.Command("echo", "Executing a command in Go").CombinedOutput()
if err != nil {
  os.Stderr.WriteString(err.Error())
}
fmt.Println(string(output))

Environment variables

Expand environment variables

cmd := exec.Command("echo", os.Getenv("PATH"))
cmd := exec.Command("sh", "-c",  "echo $PATH")

Stdout stream

Manually connect a buffer to capture the commands stdout stream.

cmd := exec.Command("go", "version")
cmdOutput := &bytes.Buffer{}
cmd.Stdout = cmdOutput
err := cmd.Run()
if err != nil {
  os.Stderr.WriteString(err.Error())
}
fmt.Print(string(cmdOutput.Bytes()))

Exit codes

Output the exit codes of a command

cmd := exec.Command("ls", "/foo/bar")
var waitStatus syscall.WaitStatus
if err := cmd.Run(); err != nil {
  if err != nil {
    os.Stderr.WriteString(fmt.Sprintf("Error: %s\n", err.Error()))
  }
  if exitError, ok := err.(*exec.ExitError); ok {
    waitStatus = exitError.Sys().(syscall.WaitStatus)
    fmt.Printf("Output: %s\n", []byte(fmt.Sprintf("%d", waitStatus.ExitStatus())))
  }
} else {
  // Success
  waitStatus = cmd.ProcessState.Sys().(syscall.WaitStatus)
  fmt.Printf("Output: %s\n", []byte(fmt.Sprintf("%d", waitStatus.ExitStatus())))
}

Async

Execute an asynchronous command and send signals to that command's process from within the application.

cmd := exec.Command("cat", "/dev/random")
randomBytes := &bytes.Buffer{}
cmd.Stdout = randomBytes

// Start command asynchronously
err := cmd.Start()
if err != nil {
  os.Stderr.WriteString(err.Error())
}

ticker := time.NewTicker(time.Second)
go func(ticker *time.Ticker) {
  now := time.Now()
  for _ = range ticker.C {
    fmt.Printf("Ticker: %s\n", []byte(fmt.Sprintf("%s", time.Since(now))))
  }
}(ticker)

// Kill the process after 4 seconds
timer := time.NewTimer(time.Second * 4)
go func(timer *time.Timer, ticker *time.Ticker, cmd *exec.Cmd) {
  for _ = range timer.C {
    err := cmd.Process.Signal(os.Kill)
    if err != nil {
      os.Stderr.WriteString(err.Error())
    }
    ticker.Stop()
  }
}(timer, ticker, cmd)

// Wait for the command to finish
cmd.Wait()
fmt.Printf("Result: %d\n", []byte(fmt.Sprintf("%d bytes generated", len(randomBytes.Bytes()))))