Chuyển hướng đường ống stdout của tiến trình con trong Go


105

Tôi đang viết một chương trình trong Go thực thi một chương trình giống như máy chủ (cũng là Go). Bây giờ tôi muốn có stdout của chương trình con trong cửa sổ đầu cuối của mình nơi tôi bắt đầu chương trình mẹ. Một cách để thực hiện điều này là với cmd.Output()hàm, nhưng hàm này chỉ in ra stdout sau khi quá trình đã thoát. (Đó là một vấn đề vì chương trình giống như máy chủ này chạy trong một thời gian dài và tôi muốn đọc đầu ra nhật ký)

Biến outlà của type io.ReadCloservà tôi không biết mình nên làm gì với nó để đạt được nhiệm vụ của mình và tôi không thể tìm thấy bất kỳ điều gì hữu ích trên web về chủ đề này.

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

Giải thích cho mã: bỏ ghi chú Printlnhàm để lấy mã để biên dịch, tôi biết đó Println(out io.ReadCloser)không phải là một hàm có ý nghĩa.
(nó tạo ra đầu ra &{3 |0 <nil> 0}) Hai dòng này chỉ cần thiết để lấy mã để biên dịch.


1
Dòng "thực thi" của câu lệnh nhập phải là "os / executive".
evilspacepirate,

cảm ơn vì thông tin, thực sự nó chỉ là hành động trước khi go1, bây giờ của nó trong hệ điều hành. đã cập nhật nó cho go1
mbert

1
Tôi không nghĩ rằng bạn thực sự cần phải gọi io.Copytrong thói quen đi
rmonjo

Tôi không nghĩ bạn cần phải gọi cmd.Wait()hoặc for{}vòng lặp ... tại sao những thứ này lại ở đây?
weberc2

@ weberc2 cho điều này, hãy xem câu trả lời của elimisteve. Vòng lặp for không cần thiết nếu bạn chỉ muốn chạy chương trình một lần. Nhưng nếu bạn không gọi cmd.Wait (), main () của bạn có thể kết thúc trước khi chương trình được gọi của bạn kết thúc và bạn không nhận được đầu ra như mong muốn
mbert

Câu trả lời:


207

Bây giờ tôi muốn có stdout của chương trình con trong cửa sổ đầu cuối của mình nơi tôi bắt đầu chương trình mẹ.

Không cần phải rắc rối với các đường ống hoặc quy trình, điều này rất dễ dàng.

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
Ngoài ra, nếu bạn muốn lệnh lắng nghe đầu vào, bạn có thể chỉ cần thiết lập bằng cách cmd.Stdin = os.Stdinđó làm cho nó như thể bạn đã thực hiện lệnh đó theo nghĩa đen từ trình bao của mình.
Nucleon

4
Đối với những người muốn chuyển hướng đến logthay vì stdout, có một câu trả lời ở đây
Rick Smith

18

Tôi tin rằng nếu bạn nhập ioosvà thay thế này:

//fmt.Println(out)

Với cái này:

go io.Copy(os.Stdout, out)

(xem tài liệu choio.Copychoos.Stdout ), nó sẽ làm những gì bạn muốn. (Tuyên bố từ chối trách nhiệm: không được kiểm tra.)

Nhân tiện, bạn có thể cũng muốn nắm bắt lỗi tiêu chuẩn, bằng cách sử dụng phương pháp tương tự như đối với đầu ra tiêu chuẩn, nhưng với cmd.StderrPipeos.Stderr.


2
@mbert: Tôi đã sử dụng đủ các ngôn ngữ khác và đã đọc đủ về cờ vây, để có linh cảm về tính năng nào có thể tồn tại để thực hiện điều này, và ở dạng nào; thì tôi chỉ cần xem qua các tài liệu gói có liên quan (do Google tìm thấy) để xác nhận rằng linh cảm của tôi là đúng và để tìm các chi tiết cần thiết. Phần khó nhất là (1) tìm ra đầu ra tiêu chuẩn nào được gọi là ( os.Stdout) và (2) xác nhận tiền đề rằng, nếu bạn hoàn toàn không gọi cmd.StdoutPipe(), đầu ra tiêu chuẩn sẽ chuyển đến /dev/nullthay vì đầu ra tiêu chuẩn của quy trình mẹ .
ruakh

15

Đối với những người không cần điều này trong một vòng lặp, nhưng muốn đầu ra lệnh lặp lại vào thiết bị đầu cuối mà không bị cmd.Wait()chặn các câu lệnh khác:

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

Lỗi nhỏ: (Rõ ràng) bạn có thể bỏ lỡ kết quả của các goroutines bắt đầu nếu "làm những việc khác ở đây" của bạn hoàn thành nhanh hơn goroutines. Việc thoát main () cũng sẽ làm cho các goroutines kết thúc. vì vậy bạn có thể không thực sự outputing để echo trong terminal nếu bạn không đợi cmd kết thúc.
galaktor
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.