2012-05-28 20 views
38

Git'de birkaç dış komutu nasıl birleştirebilirim? Bu kodu denedim ama exit status 1 diyor bir hata alıyorum.Git'de çeşitli komutlar nasıl verilir?

package main 

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

func main() { 
    c1 := exec.Command("ls") 
    stdout1, err := c1.StdoutPipe() 
    if err != nil { 
     log.Fatal(err) 
    } 

    if err = c1.Start(); err != nil { 
     log.Fatal(err) 
    } 
    if err = c1.Wait(); err != nil { 
     log.Fatal(err) 
    } 

    c2 := exec.Command("wc", "-l") 
    c2.Stdin = stdout1 

    stdout2, err := c2.StdoutPipe() 
    if err != nil { 
     log.Fatal(err) 
    } 

    if err = c2.Start(); err != nil { 
     log.Fatal(err) 
    } 
    if err = c2.Wait(); err != nil { 
     log.Fatal(err) 
    } 

    io.Copy(os.Stdout, stdout2) 
} 

cevap

30

StdoutPipe komutunun standart çıkışa zaman komut başlar bağlı olacak bir boru döndürür. Bekleme komut çıkışını gördükten sonra boru otomatik olarak kapalı olacaktır.

(http://golang.org/pkg/os/exec/#Cmd.StdinPipe itibaren) Eğer c1.Wait yapmak aslında stdoutPipe kapatır.

I (hata yakalama ekleyin sadece bir demo!) Bir çalışma örneği yaptı:

package main 

import (
    "bytes" 
    "io" 
    "os" 
    "os/exec" 
) 

func main() { 
    c1 := exec.Command("ls") 
    c2 := exec.Command("wc", "-l") 

    r, w := io.Pipe() 
    c1.Stdout = w 
    c2.Stdin = r 

    var b2 bytes.Buffer 
    c2.Stdout = &b2 

    c1.Start() 
    c2.Start() 
    c1.Wait() 
    w.Close() 
    c2.Wait() 
    io.Copy(os.Stdout, &b2) 
} 
+5

Neden exec.Cmd.StdoutPipe yerine io.Pipe kullanılır? –

+0

Ben de io.Pipe severim, ama c1'i ayrı bir goroutine atmak benim için daha iyi çalışır. Aşağıda benim değiştirilmiş sürümüme bakın. – WeakPointer

40
package main 

import (
    "os" 
    "os/exec" 
) 

func main() { 
    c1 := exec.Command("ls") 
    c2 := exec.Command("wc", "-l") 
    c2.Stdin, _ = c1.StdoutPipe() 
    c2.Stdout = os.Stdout 
    _ = c2.Start() 
    _ = c1.Run() 
    _ = c2.Wait() 
} 
+0

Temelde aynı kodu kullanıyorum ama genellikle bir "kırık boru" hatası alıyorum. Buna neyin sebep olabileceği hakkında bir fikrin var mı? http://stackoverflow.com/q/26122072/4063955 –

+0

@AnthonyHat: Lütfen bu yorumu yeni sorunuza ekleyin, böylece bunu gördüğünüzü görebiliriz ve sizin için çalışmadı. – RickyA

+0

Bir işlem, bir boruya yazmayı denediğinde, ancak borunun diğer tarafı zaten kapatılmışsa, kırık boru oluşur. Örneğin, "wc -l", yukarıdaki örnekte "ls" bitmeden önce çıkarsa, "ls" bir Broken Pipe hatası/sinyali alır. – Matt

2

Bu tam bir çalışma örneği. Execute işlevi herhangi bir sayıda exec.Cmd örneğini (bir variadic function kullanarak) alır ve ardından stdout'un çıktısını bir sonraki komutun stdinine doğru şekilde iliştirerek bunların üzerine döngüler yapar. Bu, herhangi bir işlev çağrılmadan önce yapılmalıdır.

çağrı işlevi sonra

package main 

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

func Execute(output_buffer *bytes.Buffer, stack ...*exec.Cmd) (err error) { 
    var error_buffer bytes.Buffer 
    pipe_stack := make([]*io.PipeWriter, len(stack)-1) 
    i := 0 
    for ; i < len(stack)-1; i++ { 
     stdin_pipe, stdout_pipe := io.Pipe() 
     stack[i].Stdout = stdout_pipe 
     stack[i].Stderr = &error_buffer 
     stack[i+1].Stdin = stdin_pipe 
     pipe_stack[i] = stdout_pipe 
    } 
    stack[i].Stdout = output_buffer 
    stack[i].Stderr = &error_buffer 

    if err := call(stack, pipe_stack); err != nil { 
     log.Fatalln(string(error_buffer.Bytes()), err) 
    } 
    return err 
} 

func call(stack []*exec.Cmd, pipes []*io.PipeWriter) (err error) { 
    if stack[0].Process == nil { 
     if err = stack[0].Start(); err != nil { 
      return err 
     } 
    } 
    if len(stack) > 1 { 
     if err = stack[1].Start(); err != nil { 
      return err 
     } 
     defer func() { 
      if err == nil { 
       pipes[0].Close() 
       err = call(stack[1:], pipes[1:]) 
      } 
     }() 
    } 
    return stack[0].Wait() 
} 

func main() { 
    var b bytes.Buffer 
    if err := Execute(&b, 
     exec.Command("ls", "/Users/tyndyll/Downloads"), 
     exec.Command("grep", "as"), 
     exec.Command("sort", "-r"), 
    ); err != nil { 
     log.Fatalln(err) 
    } 
    io.Copy(os.Stdout, &b) 
} 

Mevcut bu özünden

https://gist.github.com/tyndyll/89fbb2c2273f83a074dc

İyi de boruların düzgün kapanmasını, bir döngü içinde komutlar arda çağırmayı aramaya ertelemektedir kullanıp sağlanması konusunda gider

bilmeniz gereken nokta, ~ gibi kabuk değişkenlerinin enterpolasyona uğramamasıdır.

+0

Güncellendi - savunmamda, saatlerce çalıştıktan sonra saat 5'de cevap verdim :) – Tyndyll

50

Basit senaryolar için thi'yi kullanabilirsiniz. s yaklaşım: ilk yanıt gibi

func getCPUmodel() string { 
     cmd := "cat /proc/cpuinfo | egrep '^model name' | uniq | awk '{print substr($0, index($0,$4))}'" 
     out, err := exec.Command("bash","-c",cmd).Output() 
     if err != nil { 
       return fmt.Sprintf("Failed to execute command: %s", cmd) 
     } 
     return string(out) 
} 
2

ama ilk komutla başladı ve goroutine içinde bekledi: Örneğin

bash -c "echo 'your command goes here'"

, bu fonksiyon borulu komutları kullanarak CPU model adını alır. Bu boruyu mutlu ediyor.

package main 

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

func main() { 
    c1 := exec.Command("ls") 
    c2 := exec.Command("wc", "-l") 

    pr, pw := io.Pipe() 
    c1.Stdout = pw 
    c2.Stdin = pr 
    c2.Stdout = os.Stdout 

    c1.Start() 
    c2.Start() 

    go func() { 
     defer pw.Close() 

     c1.Wait() 
    }() 
    c2.Wait() 
}