Go'da alt sürecin stdout borusunu yeniden yönlendir


105

Go'da sunucu benzeri bir program (ayrıca Go) çalıştıran bir program yazıyorum. Şimdi alt programın stdout'unu ana programı başlattığım terminal penceremde olmasını istiyorum. Bunu yapmanın bir yolu cmd.Output()işlevdir, ancak bu stdout'u yalnızca işlemden çıktıktan sonra yazdırır. (Bu bir sorun çünkü bu sunucu benzeri program uzun süre çalışıyor ve günlük çıktısını okumak istiyorum)

Değişken out, 'dir type io.ReadCloserve görevimi yerine getirmek için onunla ne yapmam gerektiğini bilmiyorum ve bu konuda web'de yararlı hiçbir şey bulamıyorum.

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

Koda açıklama: PrintlnKodu derlemek için işlevin açıklamasını kaldırın, Println(out io.ReadCloser)bunun anlamlı bir işlev olmadığını biliyorum .
(çıktıyı üretir &{3 |0 <nil> 0}) Bu iki satır sadece derlenecek kodu almak için gereklidir.


1
Import ifadesinin "exec" satırı "os / exec" olmalıdır.
evilspacepirate

bilgi için teşekkürler, aslında sadece go1 öncesi exec idi, şimdi işletim sisteminde. go1 için güncelledi
mbert

1
Aslında io.Copygo rutinleri içinde aramanız gerektiğini sanmıyorum
rmonjo

Aramana cmd.Wait()veya for{}döngüye ihtiyacın olduğunu sanmıyorum ... bunlar neden burada?
weberc2

@ weberc2'nin cevabını ortadan kaldırmak için bu bakış. Programı sadece bir kez çalıştırmak istiyorsanız for döngüsüne gerek yoktur. Ancak cmd.Wait () 'i çağırmazsanız, main (), çağrılan programınız
bitmeden bitebilir ve

Yanıtlar:


207

Şimdi alt programın stdout'unu ana programı başlattığım terminal penceremde olmasını istiyorum.

Borularla veya gorutinlerle uğraşmanıza gerek yok, bu çok kolay.

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
Ek olarak, komutun girdiyi dinlemesini istiyorsanız, basitçe ayarlayarak, cmd.Stdin = os.Stdinbu komutu gerçekten kabuğunuzdan çalıştırmışsınız gibi yapabilirsiniz.
Nucleon

4
logStandart çıkış yerine yönlendirmek isteyenler için burada
Rick Smith

18

Ben içe eğer inanıyoruz iove osbu değiştirin:

//fmt.Println(out)

Bununla:

go io.Copy(os.Stdout, out)

( içinio.Copy ve içinos.Stdout belgelere bakın ), istediğinizi yapacaktır. (Sorumluluk reddi: test edilmemiştir.)

Bu arada, muhtemelen standart-çıktıyla aynı yaklaşımı kullanarak, ancak cmd.StderrPipeve ile standart-hatayı da yakalamak isteyeceksiniz os.Stderr.


2
@mbert: Yeterince başka dil kullandım ve Go hakkında yeterince okudum, bunu yapmak için hangi özelliğin var olacağına ve yaklaşık olarak hangi biçimde olacağına dair bir önseziye sahiptim; daha sonra, önsezimin doğru olduğunu onaylamak ve gerekli ayrıntıları bulmak için ilgili paket belgelerine (Googling tarafından bulunan) bakmam gerekti. En zor kısımlar, (1) hangi standart çıktının dendiğini bulmak ( os.Stdout) ve (2), hiç arama yapmazsanız cmd.StdoutPipe(), standart çıktının /dev/nullüst sürecin standart çıktısından ziyade gideceğini onaylamaktı. .
ruakh

15

Bir döngüde buna ihtiyaç duymayanlar, ancak komut çıktısının cmd.Wait()diğer ifadeleri engellemeden terminale yankılanmasını isteyenler için :

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: (Açıkçası) "burada başka şeyler yapın" gorutinlerden daha hızlı tamamlanırsa, gorutinlerin başlattığı sonuçları kaçırabilirsiniz. Main () 'dan çıkan, gorutinlerin de bitmesine neden olacaktır. bu nedenle, cmd'nin bitmesini beklemiyorsanız, potansiyel olarak uçbirimde yankı çıktısını vermeyebilirsiniz.
galaktor
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.