2017-11-25 4 views
2

サブプロセスを使って遊んでいて、パイプを通してstdoutを読んでいる間、私は興味深い動作に気付きました。私はそのパイプがEOFに達した場合であっても永遠にハングアップからの読み込み、os/execによって作成されたサブプロセスの標準出力を読み取るためにio.Pipe()を使用する場合はEOFに達してもio.Pipe()がブロックされ続けるのはなぜですか?

(プロセスが終了する):

cmd := exec.Command("/bin/echo", "Hello, world!") 
r, w := io.Pipe() 
cmd.Stdout = w 
cmd.Start() 

io.Copy(os.Stdout, r) // Prints "Hello, World!" but never returns 

しかし、私が使用している場合組み込みメソッドStdoutPipe()それが動作:

cmd := exec.Command("/bin/echo", "Hello, world!") 
p := cmd.StdoutPipe() 
cmd.Start() 

io.Copy(os.Stdout, p) // Prints "Hello, World!" and returns 

/usr/lib/go/src/os/exec/exec.goのソースコードに掘り、私はStdoutPipe()メソッドは、実際にはない、os.Pipe()を使用していることがわかります:これは私の2つの手がかりを与える

pr, pw, err := os.Pipe() 
cmd.Stdout = pw 
cmd.closeAfterStart = append(c.closeAfterStart, pw) 
cmd.closeAfterWait = append(c.closeAfterWait, pr) 
return pr, nil 

  1. ファイル記述子は、特定の時点で閉じられています。クリティカルには、プロセスの開始後にパイプの「書き込み」終了が閉じられています。
  2. 上記で使用したようにio.Pipe()の代わりに、os.Pipe()(POSIXではおおよそpipe(2)にマップする低レベル呼び出し)が使用されます。

しかし、私の元の例が、この新しい知見を考慮した後のやり方をどうしても理解できないのです。

os.Pipe()の代わりにio.Pipe()の書き込み終了を試みると、完全に破壊され、何も読み込まれないように見えます(私がそれを渡したと思っても閉じたパイプから読み込んでいるかのように)サブプロセスへ:

cmd := exec.Command("/bin/echo", "Hello, world!") 
r, w := io.Pipe() 
cmd.Stdout = w 
cmd.Start() 

w.Close() 
io.Copy(os.Stdout, r) // Prints nothing, no read buffer available 

わかりましたので、私はio.Pipe()os.Pipe()よりもかなり異なっている、そしておそらく1 close()は皆のためにそれを閉じていないUnixのパイプのように動作しませんね。

あなたは私は簡単な修正のために求めているとは思わないのでちょうど、私はすでに私はこのコードを使用することによって、私の期待される動作を実現することができます知っている:

cmd := exec.Command("/bin/echo", "Hello, world!") 
r, w, _ := os.Pipe() // using os.Pipe() instead of io.Pipe() 
cmd.Stdout = w 
cmd.Start() 

w.Close() 
io.Copy(os.Stdout, r) // Prints "Hello, World!" and returns on EOF. Works. :-) 

私は何のために求めていることは理由でありますio.Pipe()はライターからのEOFを無視して、読者を永遠にブロックしているようですか?有効な答えは、$REASONSだから私はそれらの$REASONSが何であるか分からないので、io.Pipe()は仕事のための間違ったツールである可能性があります。ここで

は私が話しているものを説明するための完全な例です。

package main 

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

func main() { 
    cmd := exec.Command("/bin/echo", "Hello, world!") 
    r, w := io.Pipe() 
    cmd.Stdout = w 
    cmd.Start() 

    io.Copy(os.Stdout, r) // Blocks here even though EOF is reached 

    fmt.Println("Finished io.Copy()") 
    cmd.Wait() 
} 
+1

プロセスにはまだパイプの書き込み側が開いています。だからio.Copyはあなたがそれを閉じるまで待っている。これは予想される動作です。 –

+1

EOFに達していません。 EOFは、パイプを閉じるときです。 – JimB

答えて

3

「なぜio.Pipeは、()永遠にブロックするリーダーを残して、ライターからのEOFを無視するように見えるのでしょうか?」 「作家からのEOF」というものはないからです。すべてのEOFは(unixでは)読み取り側がパイプの書き込み側を開いたままにしていないことを示すものです。プロセスがライタを持たないパイプからの読み取りを試みると、readシステムコールは便利にEOFという名前の値を返します。あなたの親はまだパイプの書き込み側の1つのコピーを持っているので、readブロック。 EOFを考えるのをやめてください。単なる抽象であり、作家はそれを決して「送る」ことはありません。

関連する問題