2013-02-10 14 views
11

में है कि आदेश की 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) 
    } 
} 

उत्तर

0

Go statements

ए 'जाओ "बयान के रूप में एक समारोह या विधि कॉल नियंत्रण, या goroutine के एक स्वतंत्र समवर्ती धागे के निष्पादन शुरू होता है, ही पता स्थान के भीतर।

GoStmt = "go" अभिव्यक्ति।

अभिव्यक्ति एक कॉल होना चाहिए। फंक्शन वैल्यू और पैरामीटर कॉलिंग गोरौटाइन में सामान्य रूप से मूल्यांकन किए जाते हैं, लेकिन नियमित कॉल के विपरीत, प्रोग्राम निष्पादन पर लागू किए गए फ़ंक्शन के लिए प्रतीक्षा नहीं करता है। इसके बजाए, फ़ंक्शन नए गोरौटाइन में स्वतंत्र रूप से निष्पादित करना शुरू कर देता है। जब कार्य समाप्त हो जाता है, तो इसकी goroutine भी समाप्त हो जाती है। यदि फ़ंक्शन में कोई वापसी मान है, तो फ़ंक्शन पूरा होने पर उन्हें छोड़ दिया जाता है।

फ़ंक्शन कॉल करने के लिए gratuitous goroutines को कनवर्ट करें।

package main 

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

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) 
    } 
    populate_stdin_func(stdin) 
    io.Copy(os.Stdout, stdout) 
    err = cmd.Wait() 
    if err != nil { 
     log.Panic(err) 
    } 
} 
+2

आपका कोड काम करता है क्योंकि मेरे उदाहरण में पाइप बफ़र्स कभी नहीं भरे हुए हैं का एक और तरीका है। काम कॉल होने के लिए goroutine बदलना सामान्य रूप से काम नहीं करेगा। सामान्य मामले में, पाइप जो बिल्ली प्रक्रिया संचार करने के लिए उपयोग करती है, में एक निश्चित आकार के बफर होंगे। उदाहरण के लिए, stdin पाइप एक निश्चित बफर है। एक बार यह बफर भरने के बाद, पाइप को लिखना ब्लॉक होगा। लिनक्स पर, मुझे विश्वास है कि बफर आकार 64KiB है। Stdout के लिए बिल्ली पाइप पर भी एक ही समस्या होगी। मुख्य कोड में अवरुद्ध I/O करना मतलब है कि उन अवरुद्ध कॉल मुख्य कोड को अवरुद्ध कर देंगे। –

4

यहां आपके पहले कोड का एक संस्करण है जो काम करता है। यह सुनिश्चित करने के लिए कि आप कमांड बंद करने से पहले भेजने और प्राप्त करने के साथ पूरा करने के लिए sync.WaitGroup के अतिरिक्त को ध्यान दें।

package main 

import (
    "bytes" 
    "io" 
    "log" 
    "os" 
    "os/exec" 
    "sync" 
    "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) 
    } 
    var wg sync.WaitGroup 
    wg.Add(2) 
    go func() { 
     defer wg.Done() 
     populate_stdin_func(stdin) 
    }() 
    go func() { 
     defer wg.Done() 
     time.Sleep(5 * time.Second) 
     io.Copy(os.Stdout, stdout) 
    }() 
    wg.Wait() 
    err = cmd.Wait() 
    if err != nil { 
     log.Panic(err) 
    } 
} 

(यह सिर्फ कह क्या @peterSO कहा हालांकि ;-)

+0

यह कहने का एक और तरीका नहीं है कि @ पीटरसन ने क्या कहा। यह वास्तव में पाइप बफर को सही तरीके से संभालता है क्योंकि आउटपुट से बिल्ली को इनपुट को अलग गोरौटाइन में संभाला जाता है। मुझे यह भी लगता है कि प्रतीक्षा समूह समूह की तुलना में थोड़ा अच्छा है जो मैं सिंक्रनाइज़ेशन करता था। मुझे अभी भी यह अपेक्षाकृत भ्रमित लगता है कि पाइप cmd.Wait() के दुष्प्रभाव के रूप में बंद हैं। यह वास्तव में भ्रमित है क्योंकि प्रक्रिया समाप्त होने के बाद तक ऐसा नहीं होता है। –

 संबंधित मुद्दे

  • कोई संबंधित समस्या नहीं^_^