Piping two commands together in Go

The Go exec package makes it pretty easy to run an OS command and read the output; and there are a few examples of how to pipe the output of one command into another (e.g. lsof | wc -l):

lsof := exec.Command("lsof")
wc := exec.Command("wc", "-l")
outPipe, err := lsof.StdoutPipe()
if err != nil {
    return nil, err
}
lsof.Start()
wc.Stdin = outPipe
out, err := wc.Output()
if err != nil {
    return nil, err
}
return out, nil

There’s a problem with this code though, the pipe will never be closed; if the caller is a long-lived process then it will leak file descriptors.

You don’t normally have to worry about this, as calling Run or Output (instead of Start) will take care of it; but we can’t use those as they’ll close the pipe too early.

The problem is easily solved, either by closing the pipe yourself:

outPipe, err := lsof.StdoutPipe()
defer outPipe.Close()

or by calling Wait after the output from the second command has been received:

out, err := wc.Output()
if err != nil {
    return nil, err
}
err = lsof.Wait()
if err != nil {
    return nil, err
}
return out, nil

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s