Skip to content

How Go Exec Works

It’s common to run and wish to get results. As the command is executed in a forked child process, how results are passed back? This blog investigates how output is passed to parent process in command.Output.

A simple example is:

func main() {
    c := exec.Command("ls")
    c.Output()
}

The Output function uses bytes.Buffer to provide an io.Writer . As we know, when fork, the children will inherit all file descriptors. but the buffer is not an fd and cannot use to communicate between processes.

To communicate, the parent process wraps the command stdout, stdin, and stderr by pipelines, which could be represented by file descriptors.

  • Wrap the stdin, stdout and stderr:
// in method (c *Cmd) Start() error
for _, setupFd := range []F{(*Cmd).stdin, (*Cmd).stdout, (*Cmd).stderr} {
        fd, err := setupFd(c)
        // ignore error handling
        c.childFiles = append(c.childFiles, fd)
    }
// ignore the other variable setup
c.Process, err = os.StartProcess(c.Path, c.argv(), &os.ProcAttr{
        // ignore some fields
        Files: c.childFiles,
    })
  • cmd stdin method:
func (c *Cmd) stdin() (f *os.File, err error) {
    if c.Stdin == nil {
        f, err = os.Open(os.DevNull)
        // ignore error handling
        return
    }

    if f, ok := c.Stdin.(*os.File); ok {
        return f, nil
    }

    pr, pw, err := os.Pipe()
    // ignore error handling and some logic

    // setup the goroutine to copy data.
    c.goroutine = append(c.goroutine, func() error {
        _, err := io.Copy(pw, c.Stdin)
        // ignore error handling
    })
    return pr, nil
}

What happens in cmd Wait() method?

The command Wait method wraps the process.Wait which returns until the process exit.

Wait waits for the Process to exit, and then returns a ProcessState describing its status and an error, if any. Wait releases any resources associated with the Process.

After ending of process, it waits the done of c.goroutine,and check the errors raised by functions inside it. And then, close file descriptors and return error if there is.