Труба перенаправлення відтінку дочірнього процесу в Go


105

Я пишу програму в Go, яка виконує такий сервер, як програма (також Go). Тепер я хочу мати відтінок дочірньої програми у своєму вікні терміналу, де я запустив батьківську програму. Один із способів зробити це - за допомогоюcmd.Output() функції, але цей друкує stdout лише після завершення процесу. (Це проблема, оскільки ця серверна програма працює тривалий час, і я хочу прочитати вихідний журнал)

Змінна outє, 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
Ваш рядок "exec" у заяві про імпорт має бути "os / exec".
evilspacepirate

спасибі за інформацію, насправді це було лише exec pre go1, тепер його в OS. оновив його для go1
1313

1
Я не думаю, що вам насправді потрібно зателефонувати io.Copyв підпрограми go
rmonjo

Я не думаю, що вам потрібно дзвонити cmd.Wait()чи for{}петлю ... чому вони тут?
weberc2

@ weberc2 для цього подивіться вниз, щоб усунути відповідь. Цикл for не потрібен, якщо ви просто хочете запустити програму один раз. Але якщо ви не зателефонували на cmd.Wait (), ваш основний () може закінчитися до завершення вашої викликаної програми, і ви не отримаєте потрібний вихід
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її, зробивши так, ніби ви буквально виконали цю команду зі своєї оболонки.
Нуклеон

4
Для тих , хто хоче перенаправити logзамість стандартного виводу, є відповідь тут
Rick Smith

18

Я вважаю , що якщо імпортувати ioі osзамінити це:

//fmt.Println(out)

з цим:

go io.Copy(os.Stdout, out)

(див. документацію дляio.Copy і дляos.Stdout ), вона зробить те, що ви хочете. (Відмова від відповідальності: не перевірено.)

До речі, ви, ймовірно, захочете також захопити стандартну помилку, використовуючи той самий підхід, що і для стандартного виводу, але з cmd.StderrPipeі os.Stderr.


2
@mbert: я використав достатньо інших мов і прочитав досить про Go, щоб зрозуміти, яка функція, можливо, буде для цього, і приблизно в якій формі; тоді мені просто довелося переглянути відповідні документи-пакети (знайдені Googling), щоб підтвердити, що моя думка була правильною, і щоб знайти необхідні деталі. Найскладнішими частинами були (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")
}

Незначні фати: (Очевидно, що ви можете пропустити результати розпочатих процедур, якщо ваш "робити інші речі тут" завершиться швидше, ніж гороутини. Основний () вихід, що призведе до закінчення, також закінчується. тож ви потенційно не зможете насправді вивести відгук у терміналі, якщо не дочекаєтеся завершення cmd.
галактор
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.