में है कि आदेश की stdout से डेटा प्राप्त मैं निम्नलिखित कार्यक्रम है:ठीक से किसी आदेश को stdin पर डेटा गुजर और golang
package main
import "bytes"
import "io"
import "log"
import "os"
import "os/exec"
import "time"
func main() {
runCatFromStdinWorks(populateStdin("aaa\n"))
runCatFromStdinWorks(populateStdin("bbb\n"))
}
func populateStdin(str string) func(io.WriteCloser) {
return func(stdin io.WriteCloser) {
defer stdin.Close()
io.Copy(stdin, bytes.NewBufferString(str))
}
}
func runCatFromStdinWorks(populate_stdin_func func(io.WriteCloser)) {
cmd := exec.Command("cat")
stdin, err := cmd.StdinPipe()
if err != nil {
log.Panic(err)
}
stdout, err := cmd.StdoutPipe()
if err != nil {
log.Panic(err)
}
err = cmd.Start()
if err != nil {
log.Panic(err)
}
go populate_stdin_func(stdin)
go func() {
// Removing the following lines allow some output
// to be fetched from cat's stdout sometimes
time.Sleep(5 * time.Second)
io.Copy(os.Stdout, stdout)
}()
err = cmd.Wait()
if err != nil {
log.Panic(err)
}
}
जब एक पाश में चल रहा है, मैं कोई परिणाम नहीं मिलता है, तो जैसे:
$ while true; do go run cat_thingy.go; echo ; done
^C
यह परिणाम वर्चुअल मशीन (संस्करण go1 पर जाने के लिए) से उबंटू 12.04 पर गोलांग-गो स्थापित करने के बाद आता है। मैं मैकबुक एयर पर जाने की स्थापना पर दोहराने में सक्षम नहीं हूं (संस्करण go1.0.3 पर जाएं)। ऐसा लगता है कि किसी प्रकार की दौड़ की स्थिति है। असल में, अगर मैं नींद डालता हूं (1 * समय। दूसरा), मैं अपने कोड में यादृच्छिक नींद की कीमत पर इस मुद्दे को कभी नहीं देखता।
क्या मैं कोड में कुछ गलत कर रहा हूं, या यह एक बग है? अगर यह एक बग है, तो यह तय किया गया है?
अद्यतन: संभावित सुराग
मैंने पाया कि Command.Wait बिल्ली उपप्रक्रिया भले ही वे अभी भी अपठित डेटा है से/संवाद स्थापित करने के लिए पाइप बंद हो जाएगा। मैं वास्तव में इसे संभालने के उचित तरीके के बारे में निश्चित नहीं हूं। मुझे लगता है कि जब मैं stdin को लिख रहा हूं, तो मुझे सूचित करने के लिए एक चैनल बना सकता है, लेकिन मुझे अभी भी यह जानने की आवश्यकता होगी कि क्या बिल्ली प्रक्रिया समाप्त हो गई है ताकि यह सुनिश्चित किया जा सके कि इसके स्टडआउट पाइप में कुछ और नहीं लिखा जा रहा है। मुझे पता है कि मैं cmd.Process का उपयोग कर सकता हूं। प्रक्रिया समाप्त होने पर यह निर्धारित करने के लिए प्रतीक्षा करें, लेकिन cmd.Wait को कॉल करना सुरक्षित है?
अद्यतन: हो रही करीब
यहाँ कोड में एक नया कटौती है। मेरा मानना है कि यह stdout से stdin और पढ़ने के लिए लिखने के रूप में काम करता है। मुझे लगता है कि मैं डेटा को सही तरीके से स्ट्रीम कर सकता हूं (इसे सभी को बफर करने के बजाय) यदि मैं io.Copy को stdout हैंडलिंग goroutine से स्ट्रीम करता हूं जो कुछ स्ट्रीम करता है।
package main
import "bytes"
import "fmt"
import "io"
import "log"
import "os/exec"
import "runtime"
const inputBufferBlockLength = 3*64*(2<<10) // enough to be bigger than 2x the pipe buffer of 64KiB
const numInputBlocks = 6
func main() {
runtime.GOMAXPROCS(5)
runCatFromStdin(populateStdin(numInputBlocks))
}
func populateStdin(numInputBlocks int) func(io.WriteCloser, chan bool) {
return func(stdin io.WriteCloser) {
defer stdin.Close()
repeatedByteBases := []string{"a", "b", "c", "d", "e", "f"}
for i := 0; i < numInputBlocks; i++ {
repeatedBytes := bytes.NewBufferString(repeatedByteBases[i]).Bytes()
fmt.Printf("%s\n", repeatedBytes)
io.Copy(stdin, bytes.NewReader(bytes.Repeat(repeatedBytes, inputBufferBlockLength)))
}
}
}
func runCatFromStdin(populate_stdin_func func(io.WriteCloser)) {
cmd := exec.Command("cat")
stdin, err := cmd.StdinPipe()
if err != nil {
log.Panic(err)
}
stdout, err := cmd.StdoutPipe()
if err != nil {
log.Panic(err)
}
err = cmd.Start()
if err != nil {
log.Panic(err)
}
go populate_stdin_func(stdin)
output_done_channel := make(chan bool)
go func() {
out_bytes := new(bytes.Buffer)
io.Copy(out_bytes, stdout)
fmt.Printf("%s\n", out_bytes)
fmt.Println(out_bytes.Len())
fmt.Println(inputBufferBlockLength*numInputBlocks)
output_done_channel <- true
}()
<-output_done_channel
err = cmd.Wait()
if err != nil {
log.Panic(err)
}
}
आपका कोड काम करता है क्योंकि मेरे उदाहरण में पाइप बफ़र्स कभी नहीं भरे हुए हैं का एक और तरीका है। काम कॉल होने के लिए goroutine बदलना सामान्य रूप से काम नहीं करेगा। सामान्य मामले में, पाइप जो बिल्ली प्रक्रिया संचार करने के लिए उपयोग करती है, में एक निश्चित आकार के बफर होंगे। उदाहरण के लिए, stdin पाइप एक निश्चित बफर है। एक बार यह बफर भरने के बाद, पाइप को लिखना ब्लॉक होगा। लिनक्स पर, मुझे विश्वास है कि बफर आकार 64KiB है। Stdout के लिए बिल्ली पाइप पर भी एक ही समस्या होगी। मुख्य कोड में अवरुद्ध I/O करना मतलब है कि उन अवरुद्ध कॉल मुख्य कोड को अवरुद्ध कर देंगे। –