Go에서 자식 프로세스의 stdout 파이프 리디렉션


105

나는 프로그램 (또한 Go)과 같은 서버를 실행하는 프로그램을 Go로 작성하고 있습니다. 이제 부모 프로그램을 시작한 터미널 창에서 자식 프로그램의 표준 출력을 원합니다. 이를 수행하는 한 가지 방법은 cmd.Output()함수를 사용하는 것이지만 프로세스가 종료 된 후에 만 ​​stdout을 인쇄합니다. (이 서버와 같은 프로그램이 오랫동안 실행되고 로그 출력을 읽고 싶기 때문에 문제입니다)

변수 out는 of type io.ReadCloser이고 내 작업을 수행하기 위해 무엇을해야하는지 모르겠으며이 주제에 대한 웹에서 유용한 정보를 찾을 수 없습니다.

func main() {
    cmd := exec.Command("/path/to/my/child/program")
    out, err := cmd.StdoutPipe()
    if err != nil {
        fmt.Println(err)
    }
    err = cmd.Start()
    if err != nil {
        fmt.Println(err)
    }
    //fmt.Println(out)
    cmd.Wait()
} 

코드 설명 : Println컴파일 할 코드를 얻으려면 함수의 주석 처리를 제거하십시오 Println(out io.ReadCloser). 의미있는 함수가 아님을 알고 있습니다.
(출력을 생성합니다 &{3 |0 <nil> 0})이 두 줄은 코드를 컴파일하는 데 필요합니다.


1
import 문의 "exec"줄은 "os / exec"여야합니다.
evilspacepirate 2013 년

정보 주셔서 감사합니다. 실제로는 exec pre go1이었고 이제는 os에 있습니다. go1에 대한 업데이트
mbert

1
나는 당신이 실제로 호출 할 필요가 있다고 생각하지 않는다 io.Copy이동 루틴 내에서
rmonjo

전화 cmd.Wait()for{}루프 를 할 필요가 없다고 생각합니다 ... 왜 여기에 있습니까?
weberc2

@ weberc2는 elimisteve의 대답을 내려다 봅니다. 프로그램을 한 번만 실행하려는 경우에는 for 루프가 필요하지 않습니다. 그러나 cmd.Wait ()를 호출하지 않으면 호출 된 프로그램이 완료되기 전에 main ()이 종료 될 수 있으며 원하는 출력을 얻지 못합니다
mbert

답변:


207

이제 부모 프로그램을 시작한 터미널 창에서 자식 프로그램의 표준 출력을 원합니다.

파이프 나 고 루틴을 엉망으로 만들 필요가 없습니다. 이것은 쉽습니다.

func main() {
    // Replace `ls` (and its arguments) with something more interesting
    cmd := exec.Command("ls", "-l")
    cmd.Stdout = os.Stdout
    cmd.Stderr = os.Stderr
    cmd.Run()
}

4
또한 명령이 입력을 수신하도록하려면 간단히 설정 cmd.Stdin = os.Stdin하여 마치 쉘에서 해당 명령을 실행 한 것처럼 만들 수 있습니다 .
Nucleon 2014 년

4
logstdout 대신 리디렉션하려는 사람들을 위해 여기
Rick Smith

18

나는 이것을 수입 하고 교체 한다면 다음 io과 같이 믿는다 os.

//fmt.Println(out)

이것으로 :

go io.Copy(os.Stdout, out)

(문서 참조 에 대한io.Copy를 들어os.Stdout , 당신이 원하는 것을 할 것입니다). (면책 조항 : 테스트되지 않음)

그건 그렇고, 당신은 아마 만에, 표준 출력과 동일한 방법을 사용하여뿐만 아니라 표준 오류를 캡처 할 수 있습니다 cmd.StderrPipeos.Stderr.


2
@mbert : 저는 다른 언어를 충분히 사용했고 Go에 대해 충분히 읽었으며이를 수행하기 위해 존재할 가능성이있는 기능과 대략 어떤 형태로 존재할 것인지에 대한 예감이있었습니다. 그런 다음 관련 패키지 문서 (인터넷 검색에서 찾은)를 살펴보고 내 직감이 올바른지 확인하고 필요한 세부 정보를 찾아야했습니다. 가장 어려운 부분은 (1) 표준 출력이 무엇인지 () 찾고 os.Stdout(2) 전혀 호출하지 않으면 cmd.StdoutPipe()표준 출력이 /dev/null부모 프로세스의 표준 출력이 아니라 표준 출력으로 이동 한다는 전제를 확인하는 것입니다 .
ruakh

15

루프에서 이것을 필요로하지 않지만 cmd.Wait()다른 명령문 을 차단 하지 않고 명령 출력이 터미널로 에코되도록하려는 사람들을 위해 :

package main

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

func checkError(err error) {
    if err != nil {
        log.Fatalf("Error: %s", err)
    }
}

func main() {
    // Replace `ls` (and its arguments) with something more interesting
    cmd := exec.Command("ls", "-l")

    // Create stdout, stderr streams of type io.Reader
    stdout, err := cmd.StdoutPipe()
    checkError(err)
    stderr, err := cmd.StderrPipe()
    checkError(err)

    // Start command
    err = cmd.Start()
    checkError(err)

    // Don't let main() exit before our command has finished running
    defer cmd.Wait()  // Doesn't block

    // Non-blockingly echo command output to terminal
    go io.Copy(os.Stdout, stdout)
    go io.Copy(os.Stderr, stderr)

    // I love Go's trivial concurrency :-D
    fmt.Printf("Do other stuff here! No need to wait.\n\n")
}

Minor fyi : (분명히) "여기에서 다른 작업 수행"이 고 루틴보다 빨리 완료되면 시작된 고 루틴의 결과를 놓칠 수 있습니다. main ()이 종료되면 고 루틴도 종료됩니다. 따라서 cmd가 끝날 때까지 기다리지 않으면 잠재적으로 터미널에서 에코로 실제로 출력되지 않을 수 있습니다.
galaktor
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.