#development #golang #pattern #terminal
Sometimes, you want to execute a system command from within a Go app and process it's output line-by-line in a streaming fashion.
We of course want to avoid that we need to buffer all the output, wait for the command to finish and then process each line.
We also want to check in the end if the command ran succesfully or not and we also want to capture both standard out and standard error.
Well, here's an example that shows exactly how to do this:
1package main
2
3import (
4 "bufio"
5 "os/exec"
6
7 "github.com/pieterclaerhout/go-log"
8)
9
10func main() {
11
12 // Print the log timestamps
13 log.PrintTimestamp = true
14
15 // The command you want to run along with the argument
16 cmd := exec.Command("brew", "info", "golang")
17
18 // Get a pipe to read from standard out
19 r, _ := cmd.StdoutPipe()
20
21 // Use the same pipe for standard error
22 cmd.Stderr = cmd.Stdout
23
24 // Make a new channel which will be used to ensure we get all output
25 done := make(chan struct{})
26
27 // Create a scanner which scans r in a line-by-line fashion
28 scanner := bufio.NewScanner(r)
29
30 // Use the scanner to scan the output line by line and log it
31 // It's running in a goroutine so that it doesn't block
32 go func() {
33
34 // Read line by line and process it
35 for scanner.Scan() {
36 line := scanner.Text()
37 log.Info(line)
38 }
39
40 // We're all done, unblock the channel
41 done <- struct{}{}
42
43 }()
44
45 // Start the command and check for errors
46 err := cmd.Start()
47 log.CheckError(err)
48
49 // Wait for all output to be processed
50 <-done
51
52 // Wait for the command to finish
53 err = cmd.Wait()
54 log.CheckError(err)
55
56}
The full source code can be found here.
If this post was enjoyable or useful for you, please share it! If you have comments, questions, or feedback, you can email my personal email. To get new posts, subscribe use the RSS feed.