サブプロセスを使って遊んでいて、パイプを通して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
:
- ファイル記述子は、特定の時点で閉じられています。クリティカルには、プロセスの開始後にパイプの「書き込み」終了が閉じられています。
- 上記で使用したように
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()
}
プロセスにはまだパイプの書き込み側が開いています。だからio.Copyはあなたがそれを閉じるまで待っている。これは予想される動作です。 –
EOFに達していません。 EOFは、パイプを閉じるときです。 – JimB