1

I started learning and playing around with Go to see what it is like to make some more complex console/cli type tools instead of using shells or Python. I want to execute commands and display the output. I figured out how to print the output like this:

out, err := exec.Command("pwd").Output()
print(string(out))

Is there a way to execute the commands and have it default to stdout like a shell script, or do I need to make a helper function for this?

Update: After getting IntelliJ and the Go plugin, I poked around in the Go source and agree there is currently no way to do with without a helper method.

It is not possible to reuse a Cmd object as per this comment in the exec.go source code:

// A Cmd cannot be reused after calling its Run, Output or CombinedOutput
// methods.

I did incorporate the stdout option into my own helper, including other options like shell integration. I will try turn that into open source if I can make it useful. An interesting first day of Go.

2 Answers 2

3

The solution

Actually, it is pretty easy. You can set the stdout of the command to os.Stdout and Bob's your uncle:

package main

import (
  "os"
  "os/exec"
)

func main() {

  cmd := exec.Command("pwd")

  cmd.Stdout = os.Stdout

  err := cmd.Run()

  if err != nil {
    panic(err)
  }
}

What's happening here?

By default, the output of a command is stored in a bytes.Buffer if cmd.Stdout is not set to another io.Writer. The call of cmd.Output() then runs the command and saves the output to said buffer.

Since os.Stdout implements io.Writer interface, we simply set cmd.Stdout to be os.Stdout. Now when .Run() is called, the output of the command gets written to the io.Writer defined in cmd.Stdout, which happens to be os.Stdout and the output gets written in the shell.

EDIT: As per comment, if all commands should write to os.Stdout, there of course is no way to prevent some helper. I'd do it like this:

package main 

import (
 "os"
 "os/exec"
)

func CmdToStdout( c string ) (err error){
  cmd := exec.Command(c)
  cmd.Stdout = os.Stdout
  err = cmd.Run()
  return
}

func main() {

  err := CmdToStdout("pwd")
  if err != nil {
    panic(err)
  }
}
Sign up to request clarification or add additional context in comments.

4 Comments

this will output it only for this current command. If you will run the next exec.command you will still need to repeat the step. OP wanted to make it run all the time without repetition.
I haven't read it that way. But I think your solution needs repetition, too ;) The only difference is that my solution needs a repetition during setup, while your solution needs repetition during output and needs more RAM because of storing the output in a []byte instead of having it copied ;) However, I will add an according way.
yes, this is why I told that it is not possible. So if a couple of lines is just a huge burden the only way it to create a helper.
I was hoping to set it once for many calls. The answer is interesting and I did notice the docs mention a Cmd can only be run once, hence dashing my hopes of reusing the Cmd. It does seem more efficient to be able to push it straight to os.Stdout without an intermediate step, so I will try this.
1

You have to create a helper if you need this often (and 5 lines looks too much). Based on the documentation this is a recommended way:

package main

import (
    "fmt"
    "log"
    "os/exec"
)

func main() {
    out, err := exec.Command("date").Output()
    if err != nil {
        log.Fatal(err)
    }
    fmt.Printf("The date is %s\n", out)
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.