best way to exec shell commands

5,146 views
Skip to first unread message

m

unread,
Aug 5, 2010, 6:40:28 PM8/5/10
to golang-nuts
I see that there are several ways to execute shell commands.

os.ForkExec
exec.Run
syscall.Exec

Which is the best one for this use case: I execute a command from a
string (no arguments) and get the stdout returned in a string.

Thank you.

Andrew Gerrand

unread,
Aug 5, 2010, 7:31:56 PM8/5/10
to m, golang-nuts

exec.Run is an easy-to-use a wrapper for os.ForkExec, which is itself
a wrapper for syscall.ForkExec.

Use exec.Run. Here's a snippet of code that reads the stdout of a
command into a bytes.Buffer:

p, err := exec.Run(cmd, args, nil, "",
exec.DevNull, exec.Pipe, exec.PassThrough)
if err != nil {
return
}
b := bytes.NewBuffer([]byte{})
_, err = b.ReadFrom(p.Stdout)
if err != nil {
return
}
err = p.Close()
if err != nil {
return
}

Andrew

Igor Almeida

unread,
Aug 5, 2010, 7:40:54 PM8/5/10
to golang-nuts
It depends on how low-level you want to go. If you take a look at the
source code, exec.Run calls os.ForkExec internally, which itself calls
syscall.ForkExec (which is not the same thing as syscall.Exec).
You should probably stick with exec.Run and set up std{in,out,err}
configuration to do what you want.

Hope it helps.

Andrew Gerrand

unread,
Aug 5, 2010, 7:46:49 PM8/5/10
to m, golang-nuts
By the way, this line:

>        b := bytes.NewBuffer([]byte{})

could more simply be:

       var b bytes.Buffer

Andrew

m

unread,
Aug 5, 2010, 7:49:51 PM8/5/10
to golang-nuts
The following code prints nothing out...


package main

import (
"fmt"
"exec"
"bytes"
)

func main() {
cmd := "cd /Users/m/Documents/myapp/ && /usr/bin/make"
var args []string

p, err := exec.Run(cmd, args, nil, "",
exec.DevNull, exec.Pipe, exec.PassThrough)
if err != nil {
return
}
b := bytes.NewBuffer([]byte{})
_, err = b.ReadFrom(p.Stdout)
if err != nil {
return
}
err = p.Close()
if err != nil {
return
}
output := b.String()
fmt.Println(output)
}

Andrew Gerrand

unread,
Aug 5, 2010, 8:09:37 PM8/5/10
to m, golang-nuts
Some changes were required. You wish to run shell commands, so you
must execute a shell (here, /bin/sh), and pass your commands as an
argument, eg:

/bin/sh -c "cd / && ls"

Also, the first argument to args should be the name of the command.
(analogous to argv[0] being the name of the running program)

Here's a working program:

package main

import (
"fmt"
"exec"
"bytes"
)

func main() {
cmd := "/bin/sh"
args := []string{cmd, "-c", "ls passwd"}
dir := "/etc"

p, err := exec.Run(cmd, args, nil, dir,


exec.DevNull, exec.Pipe, exec.PassThrough)
if err != nil {
return
}

var b bytes.Buffer


_, err = b.ReadFrom(p.Stdout)
if err != nil {
return
}
err = p.Close()
if err != nil {
return
}

fmt.Println(b.String())

m

unread,
Aug 5, 2010, 8:20:29 PM8/5/10
to golang-nuts
I'm trying to do a make. Which is obviously different with go than
normal make. There is a bash profile that makes all of that work..
but it doesn't work when executed this way.

Andrew Gerrand

unread,
Aug 5, 2010, 8:22:30 PM8/5/10
to m, golang-nuts
On 6 August 2010 10:20, m <phill...@gmail.com> wrote:
> I'm trying to do a make.  Which is obviously different with go than
> normal make.  There is a bash profile that makes all of that work..
> but it doesn't work when executed this way.

Then you should populate the 'env' argument to Run with the relevant
environment variables. Or write a shell script which does the same.

Andrew

Reply all
Reply to author
Forward
0 new messages