Skip to content

Add shell command to capture and stream output #3

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 7, 2017
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions shell/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"os"
"strings"
"github.com/gruntwork-io/gruntwork-cli/errors"
"io"
"bufio"
)

// Run the specified shell command with the specified arguments. Connect the command's stdin, stdout, and stderr to
Expand Down Expand Up @@ -37,6 +39,72 @@ func RunShellCommandAndGetOutput(options *ShellOptions, command string, args ...
return string(out), errors.WithStackTrace(err)
}

// Run the specified shell command with the specified arguments. Return its stdout and stderr as a string and also
// stream stdout and stderr to the OS stdout/stderr
func RunShellCommandAndGetAndStreamOutput(options *ShellOptions, command string, args ... string) (string, error) {
options.Logger.Infof("Running command: %s %s", command, strings.Join(args, " "))

cmd := exec.Command(command, args...)

cmd.Dir = options.WorkingDir

cmd.Stdin = os.Stdin

stdout, err := cmd.StdoutPipe()
if err != nil {
return "", errors.WithStackTrace(err)
}

stderr, err := cmd.StderrPipe()
if err != nil {
return "", errors.WithStackTrace(err)
}

if err := cmd.Start(); err != nil {
return "", errors.WithStackTrace(err)
}

output, err := readStdoutAndStderr(stdout, stderr, options)
if err != nil {
return output, err
}

err = cmd.Wait()
return output, errors.WithStackTrace(err)
}

// This function captures stdout and stderr while still printing it to the stdout and stderr of this Go program
func readStdoutAndStderr(stdout io.ReadCloser, stderr io.ReadCloser, options *ShellOptions) (string, error) {
allOutput := []string{}

stdoutScanner := bufio.NewScanner(stdout)
stderrScanner := bufio.NewScanner(stderr)

for {
if stdoutScanner.Scan() {
text := stdoutScanner.Text()
options.Logger.Println(text)
allOutput = append(allOutput, text)
} else if stderrScanner.Scan() {
text := stderrScanner.Text()
options.Logger.Println(text)
allOutput = append(allOutput, text)
} else {
break
}
}

if err := stdoutScanner.Err(); err != nil {
return "", errors.WithStackTrace(err)
}

if err := stderrScanner.Err(); err != nil {
return "", errors.WithStackTrace(err)
}

return strings.Join(allOutput, "\n"), nil
}

// Return true if the OS has the given command installed
func CommandInstalled(command string) bool {
_, err := exec.LookPath(command)
Expand Down