Skip to main content
While spawning processes creates new processes alongside your Go program, sometimes you want to completely replace the current Go process with another one. This is where Go’s implementation of the classic Unix exec function comes in.
What is exec?The exec system call replaces the current process image with a new process image. This is different from spawning, where both processes continue to run.

How Exec Works

When you exec a process:
  1. The current Go process ends
  2. The new process takes its place
  3. The new process inherits the same process ID
  4. If successful, code after the exec call never executes

Basic Example

Here’s how to exec the ls command:
package main

import (
    "os"
    "os/exec"
    "syscall"
)

func main() {
    // Find the absolute path to the binary
    binary, lookErr := exec.LookPath("ls")
    if lookErr != nil {
        panic(lookErr)
    }

    // Prepare arguments (first should be program name)
    args := []string{"ls", "-a", "-l", "-h"}

    // Get current environment variables
    env := os.Environ()

    // Replace current process with ls
    execErr := syscall.Exec(binary, args, env)
    if execErr != nil {
        panic(execErr)
    }

    // This line never executes if exec succeeds
}

Step-by-Step Breakdown

1

Find the binary

Use exec.LookPath to get the absolute path to the executable. Go requires absolute paths for exec.
binary, lookErr := exec.LookPath("ls")
// Returns something like "/bin/ls"
2

Prepare arguments

Arguments must be provided as a slice. The first argument should be the program name.
args := []string{"ls", "-a", "-l", "-h"}
3

Set environment

Provide environment variables for the new process. Usually you’ll pass your current environment.
env := os.Environ()
4

Execute the call

Call syscall.Exec. If successful, your program ends here and is replaced by the new process.
execErr := syscall.Exec(binary, args, env)

Key Requirements

Go requires an absolute path to the binary. Use exec.LookPath to find executables in your system’s PATH.
// Good
binary, _ := exec.LookPath("ls")
syscall.Exec(binary, args, env)

// Bad - will fail
syscall.Exec("ls", args, env)
Unlike some shell commands, arguments must be provided as a string slice, not a single string.
// Good
args := []string{"ls", "-a", "-l", "-h"}

// Bad
args := "ls -a -l -h"  // This won't work
By convention, the first element in the arguments slice should be the name of the program being executed.
args := []string{"ls", "-a", "-l", "-h"}
//                ^^^^-- program name

Example Output

When you run this program, it gets replaced by ls:
$ go run execing-processes.go
total 16
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 execing-processes.go
Notice that the output is from ls, not from your Go program.

No Fork in Go

Go does not offer a classic Unix fork function. However, this usually isn’t an issue because:
  • Starting goroutines handles concurrent execution
  • Spawning processes creates child processes
  • Execing processes replaces the current process
These three mechanisms cover most use cases for fork.

When to Use Exec vs Spawn

Use Exec When

  • You want to replace your Go program entirely
  • Building a wrapper or launcher program
  • Implementing a process supervisor that becomes the supervised process

Use Spawn When

  • You need to run a command and continue your Go program
  • You want to capture the command’s output
  • You need to run multiple commands

Spawning Processes

Create new processes alongside your Go program

Environment Variables

Work with environment variables in Go

Build docs developers (and LLMs) love