Home Execute Shell/Bash in Go
Reply: 1

Execute Shell/Bash in Go

Artur Rychlewicz
1#
Artur Rychlewicz Published in 2017-12-07 16:05:08Z

I am trying to start a shell process in Go. This is my code:

func StartShell() (*Shell, error) {
    cmd := exec.Command("sh")

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

    stdin, err := cmd.StdinPipe()
    if err != nil {
        return nil, err
    }

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

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

    sh := &Shell{
        cmd:       cmd,
        stderr:    stderr,
        stdin:     stdin,
        stdout:    stdout,
    }

    // this will read forever, but it is only for ilustrative purposes
    for {
        b := make([]byte, 2048)
        fmt.Println(stdout.Read(b))
    }

    return sh, nil
}

If I execute above function as it is, I will not get any output, however if I change sh to cmd and execute it on Windows, it prints some data at the beginning and waits forever (that is OK). Example output:

36 <nil>
129 <nil>

This led me to thinking that it may be Linux way. So I changed the command to ls and executed it on Linux. In short: it works as expected.

Next, I redirected stdout, stderr and stdin to the process ones. I've added following snippet just above cmd.Start():

// without those three below, `sh` would not print anything to stdout
cmd.Stdout = os.Stdout
cmd.Stdin = os.Stdin
cmd.Stderr = os.Stderr

When executed now, I have fully operational sh process redirected to my terminal. It only works when all 3 streams are redirected.

Why does this happen? Ideally, I'd like to start shell process in a background and run commands from within that shell without printing it to terminal. (I am aware that this is not optimal solution.)

maerics
2#
maerics Reply to 2017-12-07 16:28:47Z

On some systems a process will not terminate until it can flush its data to stdout/stderr so it's necessary to drain those streams entirely before the process will exit. Similarly, some processes will try to read stdin entirely, so they nead a stream to read before the process will exit.

If you don't want to print the subprocess output to the standard streams then consider redirecting them to another stream that will consume their data (e.g. a byte buffer or a discard writer).

cmd.Stdin = strings.NewReader("") // Empty string goes to stdin

out := bytes.NewBuffer([]byte{}) 
cmd.Stdout = out // Put stdout into a new byte buffer

cmd.Stderr = ioutil.Discard // Discard stderr
You need to login account before you can post.

About| Privacy statement| Terms of Service| Advertising| Contact us| Help| Sitemap|
Processed in 0.324467 second(s) , Gzip On .

© 2016 Powered by mzan.com design MATCHINFO