Как я могу получить stdin для exec cmd в golang

У меня есть этот код

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)
}

а затем я делаю os.Exit(1), чтобы остановить основной процесс

Я могу получить вывод из подпроцесса

но я также хочу поставить stdin в

Я пытаюсь

subProcess.Stdin = os.Stdin

но он не работает

Ответ 1

Я сделал простую программу (для тестирования). Он считывает число и записывает указанный номер.

package main

import (
    "fmt"
)

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

И вот модифицированный код

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
}

Вам интересны эти строки

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

Объяснение вышеприведенных строк

  • Мы получаем подпроцесс 'stdin, теперь мы можем написать ему
  • Мы используем свою силу, и мы пишем число
  • Мы ожидаем завершения нашего подпроцесса

Выход

START
Привет, Какой ваш любимый номер? Мне тоже нравится 4. END

Для лучшего понимания

Ответ 2

Хотя этот вопрос немного стар, но вот мой ответ:

Этот вопрос, конечно, очень специфичен для платформы, так как управление стандартным IO зависит от реализации ОС, а не от языка Go. Однако, как общее правило (из-за преобладания некоторых ОС), "то, что вы просите, невозможно".

В большинстве современных операционных систем вы можете подключать стандартные потоки (как в ответе @mraron), вы можете отделить их (так работают демоны), но вы не можете переназначить или делегировать их другому процессу.

Я думаю, что это ограничение больше связано с соображениями безопасности. Есть все еще время от времени обнаруженные ошибки, которые позволяют удаленное выполнение кода, представьте, могла ли ОС переназначить/делегировать STDIN/OUT, тогда с такими уязвимостями последствия были бы катастрофическими.

Ответ 3

Пока вы не можете напрямую это сделать, поскольку @AlexKey написал ранее, вы можете сделать некоторые обходные пути. Если os не позволяет вам транслировать свои собственные стандартные потоки, которые волнуют все, что вам нужно, 2 канала и 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

тогда вам понадобятся две функции, как я упоминал ранее

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

Та же идея для stdout