2009-07-20 18 views
80

में बाहरी प्रक्रिया के STDOUT से निरंतर पढ़ा जाता है, मैं एक रूबी स्क्रिप्ट के माध्यम से कमांड लाइन से ब्लेंडर चलाने के लिए चाहता हूं, जो फिर जीयूआई में प्रगति पट्टी को अपडेट करने के लिए ब्लेंडर लाइन द्वारा दिए गए आउटपुट को संसाधित करेगा। यह वास्तव में महत्वपूर्ण नहीं है कि ब्लेंडर बाहरी प्रक्रिया है जिसका स्टडआउट मुझे पढ़ने की जरूरत है।रुबी

मैं प्रोजेक्ट संदेश ब्लेंडर को पकड़ने में सक्षम नहीं लग सकता है, आमतौर पर जब ब्लेंडर प्रक्रिया चल रही है, तो मैंने सामान्य रूप से खोल को प्रिंट किया है, और मैंने कुछ तरीकों से प्रयास किया है। मैं हमेशा ब्लेंडर के बाद ब्लेंडर के stdout तक पहुंचने लग रहा हूं, यह अभी भी चल रहा है, जबकि नहीं।

यहां एक असफल प्रयास का एक उदाहरण दिया गया है। यह हो जाते हैं और ब्लेंडर के उत्पादन के पहले 25 लाइनों मुद्रित करता है, लेकिन केवल तब ब्लेंडर प्रक्रिया से बाहर निकल गया है:

blender = nil 
t = Thread.new do 
    blender = open "| blender -b mball.blend -o //renders/ -F JPEG -x 1 -f 1" 
end 
puts "Blender is doing its job now..." 
25.times { puts blender.gets} 

संपादित करें:

यह थोड़ा स्पष्ट बनाने के लिए, आदेश लागू ब्लेंडर खोल में आउटपुट की एक धारा वापस देता है, प्रगति का संकेत देता है (भाग 1-16 पूरा आदि)। ऐसा लगता है कि ब्लेंडर छोड़ने तक उत्पादन को "हो जाता है" को अवरुद्ध कर दिया जाता है। मुद्दा यह है कि ब्लेंडर अभी भी चल रहा है, जबकि ब्लेंडर प्रिंट आउटपुट के रूप में इस आउटपुट तक पहुंच प्राप्त करने के लिए है।

उत्तर

161

मुझे इस समस्या को हल करने में कुछ सफलता मिली है। यहां कुछ स्पष्टीकरण के साथ विवरण दिए गए हैं, यदि किसी को भी ऐसी ही समस्या हो रही है तो यह पृष्ठ पाता है। लेकिन अगर आपको विवरण की परवाह नहीं है, तो यहां संक्षिप्त उत्तर:

PTY का उपयोग करें।निम्नलिखित तरीके से अंडे (बेशक अपने स्वयं के आदेश के साथ):

require 'pty' 
cmd = "blender -b mball.blend -o //renders/ -F JPEG -x 1 -f 1" 
begin 
    PTY.spawn(cmd) do |stdout, stdin, pid| 
    begin 
     # Do stuff with the output here. Just printing to show it works 
     stdout.each { |line| print line } 
    rescue Errno::EIO 
     puts "Errno:EIO error, but this probably just means " + 
      "that the process has finished giving output" 
    end 
    end 
rescue PTY::ChildExited 
    puts "The child process exited!" 
end 

और यहाँ, उत्तर है है जिस तरह से भी कई विवरण के साथ:

असली मुद्दा यह है कि अगर एक प्रक्रिया नहीं करता प्रतीत हो रहा है स्पष्ट रूप से अपने stdout flush नहीं है, तो प्रक्रिया को पूरा होने तक, वास्तव में भेजा जाने के बजाय stdout के लिए लिखा गया buffered है, ताकि आईओ को कम करने के लिए (यह स्पष्ट रूप से कई सी पुस्तकालयों के एक कार्यान्वयन विस्तार किया गया है, ताकि थ्रूपुट अधिकतम हो कम बार आईओ के माध्यम से)। यदि आप प्रक्रिया को आसानी से संशोधित कर सकते हैं ताकि यह नियमित रूप से stdout flushes, तो यह आपका समाधान होगा। मेरे मामले में, यह ब्लेंडर था, इसलिए स्रोत को संशोधित करने के लिए खुद को एक पूर्ण नोब के लिए थोड़ा डरा रहा था।

लेकिन जब आप इन प्रक्रियाओं को खोल से चलाते हैं, तो वे वास्तविक समय में खोल में स्टडआउट प्रदर्शित करते हैं, और stdout को buffered प्रतीत नहीं होता है। यह केवल एक ही प्रक्रिया से बुलाया जाता है जब मुझे विश्वास है, लेकिन यदि एक खोल का सामना किया जा रहा है, तो वास्तविक समय में stdout देखा जाता है, unbuffered।

यह व्यवहार एक रूबी प्रक्रिया के साथ भी देखा जा सकता है क्योंकि बच्चे की प्रक्रिया जिसका उत्पादन वास्तविक समय में एकत्र किया जाना चाहिए।

फिर एक गहरे लाल रंग का स्क्रिप्ट इसे कहते और इसके उत्पादन वापस जाने के लिए:

IO.popen("ruby random.rb") do |random| 
    random.each { |line| puts line } 
end 

आपको लगता है कि आप नहीं मिलता देखेंगे बस अनुसरण कर लाइन के साथ, एक स्क्रिप्ट, random.rb बनाने परिणामस्वरूप वास्तविक समय में आप उम्मीद कर सकते हैं, लेकिन सभी बाद में। STDOUT को buffered किया जा रहा है, भले ही आप random.rb चलाते हैं, यह buffered नहीं है। इसे random.rb में ब्लॉक के अंदर STDOUT.flush कथन जोड़कर हल किया जा सकता है। लेकिन अगर आप स्रोत नहीं बदल सकते हैं, तो आपको इसके आसपास काम करना होगा। आप प्रक्रिया के बाहर से इसे फ्लश नहीं कर सकते हैं।

यदि उपप्रोसेस रीयल-टाइम में खोल के लिए प्रिंट कर सकता है, तो वास्तविक समय में रुबी के साथ इसे कैप्चर करने का एक तरीका होना चाहिए। और वहां है। आपको पीटीई मॉड्यूल का उपयोग करना है, रूबी कोर में शामिल है, मुझे विश्वास है (1.8.6 वैसे भी)। दुख की बात यह है कि यह दस्तावेज नहीं है। लेकिन मुझे सौभाग्य से उपयोग के कुछ उदाहरण मिले।

सबसे पहले, यह समझाने के लिए कि पीटीई क्या है, यह pseudo terminal है। असल में, यह रूबी स्क्रिप्ट को उपप्रोसेसर में पेश करने की अनुमति देता है जैसे कि यह एक वास्तविक उपयोगकर्ता है जिसने कमांड को शेल में टाइप किया है। तो कोई भी परिवर्तित व्यवहार जो तब होता है जब उपयोगकर्ता ने एक खोल के माध्यम से प्रक्रिया शुरू की है (जैसे कि STDOUT को इस मामले में buffered नहीं किया जा रहा है) होगा। इस तथ्य को समझते हुए कि एक और प्रक्रिया ने इस प्रक्रिया को शुरू किया है, आपको रीयल-टाइम में STDOUT एकत्र करने की अनुमति मिलती है, क्योंकि इसे buffered नहीं किया जा रहा है।

बच्चे के रूप में random.rb स्क्रिप्ट के साथ यह काम करने के लिए निम्नलिखित कोड का प्रयास करें,:,

require 'pty' 
begin 
    PTY.spawn("ruby random.rb") do |stdout, stdin, pid| 
    begin 
     stdout.each { |line| print line } 
    rescue Errno::EIO 
    end 
    end 
rescue PTY::ChildExited 
    puts "The child process exited!" 
end 
+0

+1 - अच्छा और अच्छी तरह से किया गया! :) –

+6

यह इतना अच्छा जवाब है। मेरा दिन बना दिया। –

+7

यह बहुत अच्छा है, लेकिन मेरा मानना ​​है कि stdin और stdout ब्लॉक पैरामीटर को स्वैप किया जाना चाहिए। देखें: http://www.ruby-doc.org/stdlib-1.9.3/libdoc/pty/rdoc/PTY.html#method-c-spawn –

4

STDOUT.flush या STDOUT.sync = सच

+0

हाँ, यह एक लंगड़ा जवाब था। आपका जवाब बेहतर था। – mveerman

+0

'STDOUT.sync = true' मेरे लिए एक आकर्षण की तरह काम करता है –

12

उपयोग IO.popenThis एक अच्छा उदाहरण है।

आपका कोड बन जाएगा कुछ की तरह:

blender = nil 
t = Thread.new do 
    IO.popen("blender -b mball.blend -o //renders/ -F JPEG -x 1 -f 1") do |blender| 
    blender.each do |line| 
     puts line 
    end 
    end 
end 
+0

मैंने यह कोशिश की है। समस्या वही है। मुझे बाद में आउटपुट तक पहुंच मिलती है। मेरा मानना ​​है कि IO.popen कमांड के रूप में पहला तर्क चलाकर शुरू होता है, और इसके समाप्त होने की प्रतीक्षा करता है। मेरे मामले में, आउटपुट ब्लेंडर द्वारा दिया जाता है जबकि ब्लेंडर अभी भी प्रसंस्करण कर रहा है। और फिर ब्लॉक के बाद बुलाया जाता है, जो मेरी मदद नहीं करता है। – ehsanul

+0

यहां मैंने कोशिश की है। यह ब्लेंडर के बाद आउटपुट लौटाता है: IO.popen ("ब्लेंडर -बी mball.blend // renders/-F जेपीईजी-एक्स 1-एफ 1", "डब्ल्यू +") करें | ब्लेंडर | ब्लेंडर.चैच {| लाइन | लाइन रखता है; आउटपुट + = लाइन;} अंत – ehsanul

+3

मुझे यकीन नहीं है कि आपके मामले में क्या हो रहा है। मैंने ऊपर दिए गए कोड का परीक्षण 'हां 'के साथ किया है, एक कमांड लाइन एप्लिकेशन जो कभी _ends_ नहीं करता है, और यह काम करता है। कोड निम्नानुसार था: 'IO.popen ('yes') {| p | पी.एच {| एफ | f रखता है}} '। मुझे संदेह है कि यह ऐसा कुछ है जिसे ब्लेंडर के साथ करना है, और रूबी नहीं है। शायद ब्लेंडर हमेशा अपने STDOUT फ्लश नहीं करता है। –

4

ब्लेंडर शायद जब तक यह कार्यक्रम समाप्त हो रहा है नहीं प्रिंट लाइन टूट जाता है। इसके बजाए, यह कैरिज रिटर्न कैरेक्टर प्रिंट कर रहा है (\ r)। सबसे आसान समाधान शायद जादू विकल्प की खोज कर रहा है जो प्रगति सूचक के साथ लाइन-ब्रेक प्रिंट करता है।

समस्या यह है कि IO#gets (और कई अन्य आईओ विधियों) लाइन ब्रेक का उपयोग एक डिलीमीटर के रूप में करते हैं। जब तक वे "\ n" वर्ण (जो ब्लेंडर नहीं भेज रहा है) हिट करते हैं, तब तक वे स्ट्रीम पढ़ेंगे।

इनपुट विभाजक $/ = "\r" सेट करने या इसके बजाय blender.gets("\r") का उपयोग करने का प्रयास करें।

बीटीडब्लू, इन जैसी समस्याओं के लिए, आपको हमेशा स्ट्रिंग के भीतर किसी छिपे हुए वर्ण देखने के लिए puts someobj.inspect या p someobj (दोनों एक ही काम करते हैं) की जांच करनी चाहिए।

+1

मैंने अभी आउटपुट का निरीक्षण किया, और ऐसा लगता है कि ब्लेंडर एक लाइन ब्रेक (\ n) का उपयोग करता है, इसलिए यह मुद्दा नहीं था। वैसे भी टिप के लिए धन्यवाद, अगली बार जब मैं इस तरह कुछ डीबग कर रहा हूं, तो मैं इसे ध्यान में रखूंगा। – ehsanul

0

मैं नहीं जानता कि क्या समय ehsanul पर सवाल का जवाब Open3::pipeline_rw() अभी तक उपलब्ध नहीं था, लेकिन यह वास्तव में चीजों को आसान बनाता है।

मुझे ब्लेंडर के साथ अहसानुल की नौकरी को समझ में नहीं आया, इसलिए मैंने tar और xz के साथ एक और उदाहरण बनाया। tar स्ट्रीम को स्ट्रीम करने के लिए इनपुट फ़ाइल (ओं) जोड़ देगा, फिर xz उस stdout को ले जाएं और इसे फिर से, किसी अन्य stdout पर संकुचित करें।हमारा काम अंतिम स्टडआउट लेना और इसे अपनी अंतिम फ़ाइल में लिखना है:

require 'open3' 

if __FILE__ == $0 
    cmd_tar = ['tar', '-cf', '-', '-T', '-'] 
    cmd_xz = ['xz', '-z', '-9e'] 
    list_of_files = [...] 

    Open3.pipeline_rw(cmd_tar, cmd_xz) do |first_stdin, last_stdout, wait_threads| 
     list_of_files.each { |f| first_stdin.puts f } 
     first_stdin.close 

     # Now start writing to target file 
     open(target_file, 'wb') do |target_file_io| 
      while (data = last_stdout.read(1024)) do 
       target_file_io.write data 
      end 
     end # open 
    end # pipeline_rw 
end