2010-01-16 10 views
11

का उपयोग करके WshShell.Exec से आउटपुट कैप्चरिंग मैंने निम्नलिखित दो फ़ंक्शंस लिखे हैं, और विंडोज स्क्रिप्ट होस्ट के अंदर चल रहे जावास्क्रिप्ट से दूसरा ("कॉल एंडवेट") कॉल किया है। मेरा समग्र इरादा एक कमांड लाइन प्रोग्राम को दूसरे से कॉल करना है। यही है, मैं cscript का उपयोग कर प्रारंभिक स्क्रिप्टिंग चला रहा हूं, और फिर उस स्क्रिप्ट से कुछ और (एंट) चलाने की कोशिश कर रहा हूं।विंडोज स्क्रिप्ट होस्ट

function readAllFromAny(oExec) 
{ 
    if (!oExec.StdOut.AtEndOfStream) 
      return oExec.StdOut.ReadLine(); 

    if (!oExec.StdErr.AtEndOfStream) 
      return "STDERR: " + oExec.StdErr.ReadLine(); 

    return -1; 
} 

// Execute a command line function.... 
function callAndWait(execStr) { 
var oExec = WshShell.Exec(execStr); 
    while (oExec.Status == 0) 
{ 
    WScript.Sleep(100); 
    var output; 
    while ((output = readAllFromAny(oExec)) != -1) { 
    WScript.StdOut.WriteLine(output); 
    } 
} 

} 

दुर्भाग्य से, जब मैं अपने कार्यक्रम चलाने, मैं नहीं क्या कहा जाता कार्यक्रम कर रहा है के बारे में तत्काल प्रतिक्रिया मिलता है। इसके बजाए, आउटपुट फिट बैठता है और शुरू होता है, कभी-कभी मूल प्रोग्राम समाप्त होने तक प्रतीक्षा करता है, और कभी-कभी ऐसा लगता है कि ऐसा लगता है। मैं वास्तव में क्या करना चाहता हूं, क्या स्पॉन्ड प्रक्रिया वास्तव में कॉलिंग प्रक्रिया के रूप में एक ही स्टडऑट साझा करती है, लेकिन मुझे ऐसा करने का कोई तरीका नहीं दिखता है। बस oExec.StdOut = WScript.StdOut सेट नहीं करता है।

वहाँ प्रक्रियाओं है कि शुरू की प्रक्रिया के stdout & stderr साझा करेंगे अंडे देने के लिए एक वैकल्पिक तरीका है? मैंने "WshShell.Run() का उपयोग करने का प्रयास किया, लेकिन यह मुझे" अनुमति अस्वीकार "त्रुटि देता है। यह समस्याग्रस्त है, क्योंकि मैं अपने क्लाइंट को यह बदलने के लिए नहीं कहना चाहता कि कैसे उनके विंडोज वातावरण को मेरे प्रोग्राम को चलाने के लिए कॉन्फ़िगर किया गया है।

मैं क्या कर सकता है?

+0

मुझे नहीं लगता कि वहाँ यह करने के लिए एक रास्ता है, मैं खोज रहा है। मेरा 'समाधान' एक ऐसा फ़ंक्शन है जो एक कमांड को प्रीपेड करता है जो प्रत्येक बार कमांड कहलाता है, जैसे WShell.Exec ("% COMSPEC% setenvvars.bat और actual_program.exe") में आवश्यक वातावरण चर सेट करता है। काफी गुंजाइश – Roel

उत्तर

1

हाँ, Exec समारोह तोड़ा जा जब यह टर्मिनल उत्पादन की बात आती है

मैं एक समान कार्य function ConsumeStd(e) {WScript.StdOut.Write(e.StdOut.ReadAll());WScript.StdErr.Write(e.StdErr.ReadAll());} है कि मैं एक पाश आपसे मिलते-जुलते में फोन का उपयोग किया गया लगता है। सुनिश्चित नहीं है कि ईओएफ की जांच करना और रेखा से पढ़ने की रेखा बेहतर या बदतर है।

+0

धन्यवाद, मुझे एक ही समस्या है। यह दृष्टिकोण एकमात्र ऐसा लगता है जो काम करता है। – Roel

2

सबसे पहले, आपका लूप टी में टूट गया है टोपी यह हमेशा oExec.StdOut से पहले पढ़ने की कोशिश करता है। यदि कोई वास्तविक आउटपुट नहीं है तो यह तब तक लटका होगा जब तक कि वहां न हो। StdOut.atEndOfStream तक कोई भी StdErr आउटपुट नहीं देखेगा (संभवतः जब बच्चा समाप्त हो जाता है)। दुर्भाग्यवश, स्क्रिप्ट इंजन में गैर-अवरुद्ध I/O की कोई अवधारणा नहीं है। इसका मतलब है read पर कॉल करना और बफर में कोई डेटा नहीं होने पर इसे तत्काल वापस कर देना। इस प्रकार संभवतः इस लूप को काम करने के लिए कोई रास्ता नहीं है जैसा आप चाहते हैं। दूसरा, WShell.Run बाल प्रक्रिया के मानक I/O तक पहुंचने के लिए कोई गुण या विधियां प्रदान नहीं करता है। यह बच्चे को एक अलग खिड़की में बनाता है, जो रिटर्न कोड को छोड़कर माता-पिता से पूरी तरह से अलग होता है। हालांकि, अगर आप चाहते हैं कि बच्चे से आउटपुट देखने में सक्षम होना है तो यह स्वीकार्य हो सकता है। आप बच्चे (इनपुट) से बातचीत करने में सक्षम होंगे लेकिन केवल नई विंडो के माध्यम से (SendKeys देखें)।

ReadAll() का उपयोग करने के लिए, यह भी बदतर होगा क्योंकि यह लौटने से पहले स्ट्रीम से सभी इनपुट एकत्र करता है ताकि आप स्ट्रीम बंद होने तक कुछ भी नहीं देख सकें। मुझे नहीं पता कि exampleReadAll को एक लूप में रखता है जो एक स्ट्रिंग बनाता है, एक if (!WScript.StdIn.AtEndOfStream) अपवादों से बचने के लिए पर्याप्त होना चाहिए।

डब्ल्यूएमआई में प्रक्रिया निर्माण विधियों का उपयोग करने का एक और विकल्प हो सकता है। मानक I/O को कैसे प्रबंधित किया जाता है यह स्पष्ट नहीं है और StdIn/Out/Err के रूप में विशिष्ट धाराओं को आवंटित करने का कोई तरीका नहीं दिखता है। एकमात्र आशा यह होगी कि बच्चा माता-पिता से इन्हें विरासत में लेगा लेकिन यह वही है जो आप चाहते हैं, है ना? (यह टिप्पणी एक विचार और थोड़ा सा शोध पर आधारित है लेकिन कोई वास्तविक परीक्षण नहीं है।)

असल में, स्क्रिप्टिंग सिस्टम जटिल इंटरप्रोसेस संचार/सिंक्रनाइज़ेशन के लिए डिज़ाइन नहीं किया गया है।

नोट: उपरोक्त पुष्टि करने वाले टेस्ट स्क्रिप्ट संस्करण 5.6 का उपयोग कर Windows XP SP2 पर किए गए थे। वर्तमान में संदर्भ (5।8) मैनुअल कोई बदलाव नहीं बताता है।

3

एक और तकनीक जो इस स्थिति में मदद कर सकती है वह मानक आउटपुट के साथ कमांड की मानक त्रुटि स्ट्रीम को पुनर्निर्देशित करना है। execStr स्ट्रिंग के अंत में "% comspec%/c" और "2> & 1" जोड़कर ऐसा करें।

zzz 

लिए:

%comspec% /c zzz 2>&1 

"2> & 1" एक रीडायरेक्ट अनुदेश जो stderr उत्पादन का कारण बनता है (फ़ाइल वर्णनकर्ता 2) हो रहा है है, आदेश आप से चलाने बदल StdOut स्ट्रीम (फ़ाइल वर्णनकर्ता 1) को लिखा है। आपको "% comspec%/c" भाग शामिल करने की आवश्यकता है क्योंकि यह आदेश दुभाषिया है जो कमांड लाइन रीडायरेक्ट के बारे में समझता है। देखें http://technet.microsoft.com/en-us/library/ee156605.aspx
"cmd" के बजाय "% comspec%" का उपयोग करना विंडोज संस्करणों की एक विस्तृत श्रृंखला के लिए पोर्टेबिलिटी देता है। यदि आपके कमांड में उद्धृत स्ट्रिंग तर्क शामिल हैं, तो उन्हें सही तरीके से प्राप्त करना मुश्किल हो सकता है: "/ c" के बाद उद्धरण संगत कैसे हैं, इसके लिए विनिर्देश अधूरा प्रतीत होता है।

इसके साथ, आपकी स्क्रिप्ट को केवल स्टडऑट स्ट्रीम पढ़ने की आवश्यकता है, और मानक आउटपुट और मानक त्रुटि दोनों प्राप्त होगी। मैंने इसे "नेट स्टॉप वूउज़र्व" के साथ उपयोग किया, जो सफलता पर स्टडऑट को लिखता है (यदि सेवा चल रही है) और विफलता पर StdErr (यदि सेवा पहले ही बंद हो चुकी है)।

+0

पुनर्निर्देशन के बारे में बेन के उत्तर और तर्कों पर उद्धरणों के उपयोग पर मेरी टिप्पणी देखें (निष्पादन योग्य उद्धरण नहीं होने पर किया जा सकता है) –

11

आप इस तरह से स्क्रिप्ट इंजन में StdErr और StdOut से नहीं पढ़ सकते हैं, क्योंकि कोड मास्टर बॉब के रूप में कोई गैर-अवरुद्ध आईओ नहीं है। यदि कॉल की गई प्रक्रिया StdErr पर बफर (लगभग 4 केबी) भरती है, जबकि आप StdOut से पढ़ने का प्रयास कर रहे हैं, या इसके विपरीत, तो आप डेडलॉक/हैंग करेंगे। StdOut की प्रतीक्षा करते समय आप भूखे रहेंगे और यह आपके लिए StdErr से पढ़ने के लिए प्रतीक्षा कर देगा।

व्यावहारिक समाधान इस तरह stdout में stderr पुनर्निर्देशित करना होता है:

sCommandLine = """c:\Path\To\prog.exe"" Argument1 argument2" 
Dim oExec 
Set oExec = WshShell.Exec("CMD /S /C "" " & sCommandLine & " 2>&1 """) 

दूसरे शब्दों में, CreateProcess के लिए पारित हो जाता है क्या है यह:

CMD /S /C " "c:\Path\To\prog.exe" Argument1 argument2 2>&1 " 

यह CMD.EXE, invokes जो व्याख्या कमांड लाइन /S /C एक विशेष पार्सिंग नियम का आह्वान करता है ताकि पहले और अंतिम उद्धरण को हटा दिया जा सके, और शेष के रूप में और CMD.EXE द्वारा निष्पादित किया गया। तो CMD.EXE इस कार्यान्वित:

"c:\Path\To\prog.exe" Argument1 argument2 2>&1 

जादू 2>&1 stdout में prog.exe के stderr पुनर्निर्देश। सीएमडी.एक्सईई एक्जिट कोड का प्रचार करेगा।

अब आप StdOut से पढ़कर और StdErr को अनदेखा करके सफल हो सकते हैं।

नकारात्मकता यह है कि StdErr और StdOut आउटपुट एक साथ मिश्रित हो जाते हैं। जब तक वे पहचानने योग्य हैं, आप शायद इसके साथ काम कर सकते हैं।

1

आपने इस Microsoft Support साइट पर वर्णित डेडलॉक समस्या को मारा होगा।

एक सुझाव यह हमेशा दोनों stdout और stderr से पढ़ने के लिए है। आप readAllFromAny को बदल सकता है: एक समय खुद के लिए

function readAllFromAny(oExec) 
{ 
    var output = ""; 

    if (!oExec.StdOut.AtEndOfStream) 
    output = output + oExec.StdOut.ReadLine(); 

    if (!oExec.StdErr.AtEndOfStream) 
    output = output + "STDERR: " + oExec.StdErr.ReadLine(); 

    return output ? output : -1; 
} 
संबंधित मुद्दे