Skip to content
/ exex Public

Extensions for exec.Command to automatically capture stderr on Run.

License

Notifications You must be signed in to change notification settings

inkel/exex

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

exec - Capture stderr in exec.Cmd.Run

exex provides a custom Cmd type that wraps exec.Cmd in a way that it will always capture standard error stream if execution fails with an exec.ExitError.

The standard library exec package contains a very useful API to execute commands, however, the exec.Cmd.Run and exec.Cmd.Output methods behave differently in the case of a failed execution. In particular, exec.Cmd.Run will NOT populate exec.ExitError.Stderr in the case of failure, whereas exec.Cmd.Output will do. While this is explicitly noted in the exec package documentation, it is a source of confusion even for experienced users.

Another issue with the standard library package is that if we use exec.Cmd.Output to only capture the error we will be incurring in unnecessary allocations because it uses a bytes.Buffer for capturing STDOUT, and also STDERR will be truncated. The wrappers defined in this library do not have this peformance penalization nor truncate any output.

In order to avoid importing both this package and os/exec, this package provides aliases for the variables and top-level functions os/exec provides.

Benchmarks

$ go test -benchmem -bench=. -benchtime=1000x
goos: darwin
goarch: amd64
pkg: github.com/inkel/exex
cpu: Intel(R) Core(TM) i7-9750H CPU @ 2.60GHz
BenchmarkCaptureStderr/stdlib-12         	    1000	   3263849 ns/op	   35560 B/op	      49 allocs/op
BenchmarkCaptureStderr/exex-12           	    1000	   3216620 ns/op	    2848 B/op	      39 allocs/op
BenchmarkRun-12                          	    1000	   3532978 ns/op	    2904 B/op	      39 allocs/op
BenchmarkRunContext-12                   	    1000	   3405547 ns/op	    3026 B/op	      41 allocs/op
BenchmarkRunCommand-12                   	    1000	   3588594 ns/op	    2904 B/op	      39 allocs/op
PASS
ok  	github.com/inkel/exex	17.660s

As you can see, the number of bytes and allocations per operation is drastically improved in exex.

A better comparison of the difference in performance can be done using benchstat:

# Generate benchmarks for stdlib
$ BENCHMARK=stdlib go test -benchmem -bench=CaptureStderr -benchtime=500x -count=10 > stdlib.txt

# Generate benchmarks for exex
$ BENCHMARK=exex go test -benchmem -bench=CaptureStderr -benchtime=500x -count=10 > exex.txt

# Compare the results
$ benchstat stdlib.txt exex.txt
name              old time/op    new time/op    delta
CaptureStderr-12    3.10ms ± 2%    3.11ms ± 2%     ~     (p=0.222 n=9+9)

name              old alloc/op   new alloc/op   delta
CaptureStderr-12    35.5kB ± 0%     2.9kB ± 2%  -91.91%  (p=0.000 n=9+10)

name              old allocs/op  new allocs/op  delta
CaptureStderr-12      49.0 ± 0%      39.0 ± 0%  -20.41%  (p=0.000 n=10+10)

If an image is worth a thousand words, how much worth is a benchmark?

License

See LICENSE, but basically, MIT.

About

Extensions for exec.Command to automatically capture stderr on Run.

Topics

Resources

License

Stars

Watchers

Forks

Languages