21

I have this code

subProcess := exec.Cmd{
    Path: execAble,
    Args: []string{
        fmt.Sprintf("-config=%s", *configPath),
        fmt.Sprintf("-serverType=%s", *serverType),
        fmt.Sprintf("-reload=%t", *reload),
        fmt.Sprintf("-listenFD=%d", fd),
    },
    Dir: here,
}
subProcess.Stdout = os.Stdout
subProcess.Stderr = os.Stderr
logger.Info("starting  subProcess:%s ", subProcess.Args)

if err := subProcess.Run(); err != nil {
    logger.Fatal(err)
}

and then I do os.Exit(1) to stop the main process

I can get output from the subprocess

but I also want to put stdin to

I try

subProcess.Stdin = os.Stdin

but it does not work

4 Answers 4

34

I made a simple program (for testing). It reads a number and writes the given number out.

package main

import (
    "fmt"
)

func main() {
    fmt.Println("Hello, What's your favorite number?")
    var i int
    fmt.Scanf("%d\n", &i)
    fmt.Println("Ah I like ", i, " too.")
}

And here is the modified code

package main

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

func main() {
    subProcess := exec.Command("go", "run", "./helper/main.go") //Just for testing, replace with your subProcess

    stdin, err := subProcess.StdinPipe()
    if err != nil {
        fmt.Println(err) //replace with logger, or anything you want
    }
    defer stdin.Close() // the doc says subProcess.Wait will close it, but I'm not sure, so I kept this line

    subProcess.Stdout = os.Stdout
    subProcess.Stderr = os.Stderr

    fmt.Println("START") //for debug
    if err = subProcess.Start(); err != nil { //Use start, not run
        fmt.Println("An error occured: ", err) //replace with logger, or anything you want
    }

    io.WriteString(stdin, "4\n")
    subProcess.Wait()
    fmt.Println("END") //for debug
}

You interested about these lines

stdin, err := subProcess.StdinPipe()
if err != nil {
    fmt.Println(err)
}
defer stdin.Close()
//...
io.WriteString(stdin, "4\n")
//...
subProcess.Wait()

Explanation of the above lines

  1. We gain the subprocess' stdin, now we can write to it
  2. We use our power and we write a number
  3. We wait for our subprocess to complete

Output

START
Hello, What's your favorite number?
Ah I like 4 too.
END

For better understanding

Sign up to request clarification or add additional context in comments.

4 Comments

you write to subprocess's stdin by io.WriteString
but I close my main process (by os.Exit()). so any code can't run after the subProcess start, my problem is, how to get the os's stdin to my subprocess. I just want to do some stuff by catach the ctrl - c but when the main process exit. I LOST the control on the console I start the program
I don't know how to do it, but I think if you catch the signals on your main program (==you don't os.Exit), you can send them via subProcess.Process.Signal(signal) to the subprocess. This version is easier to implement (I think, but don't know)
This code works, however, when I replace the command with "sudo", "ls" and input with the password, it doesn't seem to work. It is just stuck on password input prompt.
8

There's now an updated example available in the Go docs: https://golang.org/pkg/os/exec/#Cmd.StdinPipe

If the subprocess doesn't continue before the stdin is closed, the io.WriteString() call needs to be wrapped inside an anonymous function:

func main() {
    cmd := exec.Command("cat")
    stdin, err := cmd.StdinPipe()
    if err != nil {
        log.Fatal(err)
    }

    go func() {
        defer stdin.Close()
        io.WriteString(stdin, "values written to stdin are passed to cmd's standard input")
    }()

    out, err := cmd.CombinedOutput()
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("%s\n", out)
}

Comments

4

Though this question is a little old, but here is my answer:

This question is of course very platform specific as how standard IO is handled depends on the OS implementation and not on Go language. However, as general rule of thumb (due to some OSes being prevailing), "what you ask is not possible".

On most of modern operating systems you can pipe standard streams (as in @mraron's answer), you can detach them (this is how daemons work), but you cannot reassign or delegate them to another process.

I think this limitation is more because of security concern. There are still from time to time bugs being discovered that allow remote code execution, imagine if OS was allowing to reassign/delegate STDIN/OUT, then with such vulnerabilities the consequences would be disastrous.

Comments

2

While you cannot directly do this as @AlexKey wrote earlier still you can make some workarounds. If os prevents you to pipe your own standard streams who cares all you need 2 channels and 2 goroutines

var stdinChan chan []byte
var stdoutChan chan []byte

//when something happens in stdout of your code just pipe it to stdout chan
stdoutChan<-somehowGotDataFromStdOut

then you need two funcs as i mentioned before

func Pipein(){
    for {
        stdinFromProg.Write(<-stdInChan)
    }
}

The same idea for the stdout

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.