मैं PowerShell में बाहरी प्रक्रिया से एक चर में आउटपुट कैसे कैप्चर कर सकता हूं?


158

मैं एक बाहरी प्रक्रिया चलाना चाहता हूँ और यह PowerShell के एक चर में कमांड आउटपुट को कैप्चर करना चाहता हूँ। मैं वर्तमान में इसका उपयोग कर रहा हूं:

$params = "/verify $pc /domain:hosp.uhhg.org"
start-process "netdom.exe" $params -WindowStyle Hidden -Wait

मैंने पुष्टि की है कि कमांड निष्पादित हो रही है लेकिन मुझे आउटपुट को एक चर में कैप्चर करने की आवश्यकता है। इसका मतलब है कि मैं -RedirectOutput का उपयोग नहीं कर सकता क्योंकि यह केवल एक फ़ाइल पर रीडायरेक्ट करता है।


3
सबसे पहले और सबसे महत्वपूर्ण: निष्पादन के लिए (बाहरी रूप से) सांत्वना अनुप्रयोगों को सिंक्रोनाइज़ करने के लिए उपयोग न करें Start-Process- बस उन्हें सीधे किसी भी शेल में लागू करें ; बुद्धि के लिए netdom /verify $pc /domain:hosp.uhhg.org:। ऐसा करने से एप्लिकेशन को कॉलिंग कंसोल की मानक धाराओं से जुड़ा रहता है, जिससे इसके आउटपुट को सरल असाइनमेंट द्वारा कैप्चर किया जा सकता है $output = netdom ...। नीचे दिए गए अधिकांश उत्तर Start-Processप्रत्यक्ष निष्पादन के पक्ष में निहित हैं ।
mklement0

@ mklement0 को छोड़कर अगर कोई -Credentialपैरामीटर का उपयोग करना चाहता है
CJBS

@CJBS हां, एक अलग उपयोगकर्ता पहचान के साथ चलाने के लिए , का उपयोग Start-Processकरना आवश्यक है - लेकिन केवल तब (और यदि आप एक अलग विंडो में कमांड चलाना चाहते हैं)। और किसी को उस मामले में अपरिहार्य सीमाओं के बारे में पता होना चाहिए: आउटपुट पर कब्जा करने की कोई क्षमता नहीं है, सिवाय इसके - गैर-इंटरलेय्ड - फ़ाइलों में पाठ के माध्यम से और । -RedirectStandardOutput-RedirectStandardError
mklement0

जवाबों:


161

आपने कोशिश की है:

$OutputVariable = (Shell command) | Out-String


मैंने इसे "=" का उपयोग करके एक चर को सौंपने की कोशिश की, लेकिन मैंने आउट-स्ट्रिंग को पहले आउटपुट देने की कोशिश नहीं की। मैं कोशिश करूँगा।
एडम बर्ट्रम

10
मुझे समझ नहीं आ रहा है कि यहाँ क्या हो रहा है और यह काम करने के लिए नहीं मिल सकता है। क्या "शेल" एक पॉवरशेल कीवर्ड है? तो हम वास्तव में स्टार्ट-प्रोसेस cmdlet का उपयोग नहीं करते हैं? क्या आप कृपया एक ठोस उदाहरण दे सकते हैं (अर्थात "शेल" और / या "कमांड" को वास्तविक उदाहरण से बदलें)।
घातक डॉग

@deadlydog Shell Commandआप जो भी चलाना चाहते हैं, उसके साथ बदलें । यह इत्ना आसान है।
JNK

1
@ जल्द, आप सही कह रहे हैं। मैं मुख्य रूप से स्पष्ट कर रहा था कि आपकी टिप्पणी के कोड में उत्तर में कोड के लिए अलग कार्यक्षमता थी। मेरे जैसे शुरुआती इन जैसे व्यवहार में सूक्ष्म अंतर से फेंक सकते हैं!
सैम

1
@ मैं उसी मुद्दे पर भाग गया। यह बताता है कि ffmpeg कभी-कभी stdout के बजाय stderr को लिखेगा यदि, उदाहरण के लिए, आप -iआउटपुट फ़ाइल को निर्दिष्ट किए बिना विकल्प का उपयोग करते हैं । 2>&1कुछ अन्य उत्तरों में वर्णित आउटपुट का उपयोग करके पुनर्निर्देशित करना समाधान है।
3

159

नोट: प्रश्न में कमांड का उपयोग होता है Start-Process, जो लक्ष्य प्रोग्राम के आउटपुट पर सीधे कब्जा करने से रोकता है। आम तौर पर, सांत्वना अनुप्रयोगों को समकालिक रूप से निष्पादित करने के लिए उपयोग न करें Start-Process- बस किसी भी शेल में सीधे उन्हें लागू करें । ऐसा करने से एप्लिकेशन को कॉलिंग कंसोल की मानक धाराओं से जुड़ा रहता है, जिससे इसके आउटपुट को सरल असाइनमेंट द्वारा कैप्चर किया जा सकता है $output = netdom ..., जैसा कि नीचे दिया गया है।

मूल रूप से , बाहरी उपयोगिताओं से आउटपुट कैप्चर करना पॉवरशेल-देशी कमांड के साथ ही काम करता है (आप बाहरी उपकरणों को निष्पादित करने के बारे में एक रिफ्रेशर चाहते हैं):

$cmdOutput = <command>   # captures the command's success stream / stdout

ध्यान दें कि $cmdOutputएक प्राप्त करता है सरणी वस्तुओं की अगर <command>पैदा करता है 1 से अधिक उत्पादन वस्तु है, जो एक के मामले में बाहरी कार्यक्रम कार्यक्रम के उत्पादन से युक्त एक स्ट्रिंग सरणी का मतलब है लाइनों
यदि आप $cmdOutputहमेशा एकल - संभावित मल्टी-लाइन - स्ट्रिंग , उपयोग प्राप्त करना चाहते हैं
$cmdOutput = <command> | Out-String


करने के लिए कब्जा एक चर में उत्पादन और स्क्रीन पर प्रिंट :

<command> | Tee-Object -Variable cmdOutput # Note how the var name is NOT $-prefixed

या, यदि <command>कोई cmdlet या उन्नत फ़ंक्शन है, तो आप सामान्य पैरामीटर
-OutVariable/ का-ov
उपयोग कर सकते हैं :

<command> -OutVariable cmdOutput   # cmdlets and advanced functions only

नोट के साथ कि -OutVariable, के विपरीत अन्य स्थितियों में, $cmdOutputहै हमेशा एक संग्रह है, भले ही केवल एक वस्तु उत्पादन होता है। विशेष रूप से, सरणी-जैसा [System.Collections.ArrayList]प्रकार का एक उदाहरण लौटाया जाता है। इस विसंगति की चर्चा के लिए यह GitHub मुद्दा
देखें ।


कई कमांड से आउटपुट कैप्चर करने के लिए , या तो सबप्रेशन ( $(...)) का उपयोग करें या स्क्रिप्ट ब्लॉक ( { ... }) &या के साथ कॉल करें .:

$cmdOutput = $(<command>; ...)  # subexpression

$cmdOutput = & {<command>; ...} # script block with & - creates child scope for vars.

$cmdOutput = . {<command>; ...} # script block with . - no child scope

ध्यान दें कि सामान्य &व्यक्ति को (कॉल ऑपरेटर) एक व्यक्तिगत कमांड के साथ उपसर्ग करने की आवश्यकता होती है जिसका नाम / पथ उद्धृत है - जैसे, $cmdOutput = & 'netdom.exe' ...- प्रति से बाहरी कार्यक्रमों से संबंधित नहीं है (यह समान रूप से पॉवरशेल स्क्रिप्ट पर लागू होता है), लेकिन एक वाक्यविन्यास आवश्यकता है : पॉवरशेल डिफ़ॉल्ट रूप से अभिव्यक्ति मोड में एक उद्धृत स्ट्रिंग के साथ शुरू होने वाले बयान को पार्स करता है , जबकि कमांड (cmdlets, बाहरी कार्यक्रम, फ़ंक्शन, उपनाम) को लागू करने के लिए तर्क मोड की आवश्यकता होती है, जो कि &सुनिश्चित करता है।

के बीच $(...)और & { ... }/ में महत्वपूर्ण अंतर . { ... }यह है कि पूर्व एक पूरे के रूप में इसे वापस करने से पहले मेमोरी में सभी इनपुट एकत्र करता है , जबकि बाद वाला आउटपुट स्ट्रीम करता है, जो एक-एक करके पाइपलाइन प्रसंस्करण के लिए उपयुक्त है।


पुनर्निर्देशन भी समान रूप से काम करते हैं, लेकिन मूल रूप से (नीचे केवेट देखें):

$cmdOutput = <command> 2>&1 # redirect error stream (2) to success stream (1)

हालाँकि, बाहरी आदेशों के लिए निम्नलिखित की उम्मीद के अनुसार काम करने की अधिक संभावना है:

$cmdOutput = cmd /c <command> '2>&1' # Let cmd.exe handle redirection - see below.

बाहरी कार्यक्रमों के लिए विशिष्ट विचार :

  • बाहरी कार्यक्रम , क्योंकि वे पॉवरशेल के प्रकार प्रणाली के बाहर काम करते हैं, केवल कभी अपनी सफलता की धारा (स्टडआउट) के माध्यम से तार लौटाते हैं

  • यदि आउटपुट में 1 से अधिक पंक्ति होती है , तो PowerShell डिफ़ॉल्ट रूप से इसे स्ट्रिंग्स की एक सरणी में विभाजित करता है । अधिक सटीक रूप से, आउटपुट लाइनें एक प्रकार की श्रेणी में संग्रहीत की जाती हैं [System.Object[]]जिनके तत्व तार ( [System.String]) हैं।

  • यदि आप चाहते हैं कि आउटपुट एकल , संभावित मल्टी-लाइन स्ट्रिंग , पाइप के लिएOut-String :
    $cmdOutput = <command> | Out-String

  • साथ stdout में stderr रीडायरेक्ट कर रहा है2>&1 , तो भी करने के लिए के रूप में सफलता धारा का हिस्सा के रूप में यह कब्जा, के साथ आता है चेतावनियां :

    • बनाने के लिए 2>&1मर्ज stdout और stderr स्रोत पर , जाने cmd.exeपुनर्निर्देशन संभाल , निम्नलिखित मुहावरे का उपयोग कर:
      $cmdOutput = cmd /c <command> '2>&1' # *array* of strings (typically)
      $cmdOutput = cmd /c <command> '2>&1' | Out-String # single string

      • cmd /ccmd.exeकमांड के साथ आह्वान <command>और <command>समाप्त होने के बाद बाहर निकलता है।
      • चारों ओर एकल उद्धरण नोट करें 2>&1, जो यह सुनिश्चित करता है कि cmd.exePowerShell द्वारा व्याख्या किए जाने के बजाय पुनर्निर्देशन पारित किया गया है।
      • ध्यान दें कि शामिल होने का cmd.exeमतलब है कि वर्णों से बचने और पर्यावरण चर का विस्तार करने के लिए इसके नियम पावरशेल की अपनी आवश्यकताओं के अतिरिक्त डिफ़ॉल्ट रूप से खेलते हैं; में PS v3 + आप विशेष पैरामीटर --%(तथाकथित स्टॉप-पार्सिंग सिंबल ) का उपयोग कर सकते हैं , पावरस्टेल द्वारा शेष मापदंडों की व्याख्या को बंद करने के लिए, cmd.exeजैसे कि -स्टाइल पर्यावरण-चर संदर्भों को छोड़कर %PATH%

      • ध्यान दें कि जब से आप इस दृष्टिकोण के साथ स्रोत पर stdout और stderr का विलय कर रहे हैं , तो आप PowerShell में stdout-Origined और Stderr- मूल लाइनों के बीच अंतर नहीं कर पाएंगे ; यदि आपको इस अंतर की आवश्यकता है, तो PowerShell के अपने 2>&1पुनर्निर्देशन का उपयोग करें - नीचे देखें।

    • पावरशेल के 2>&1 पुनर्निर्देशन का उपयोग यह जानने के लिए करें कि कौन सी लाइनें किस स्ट्रीम से आई हैं :

      • Stderr आउटपुट को एरर रिकॉर्ड्स ( [System.Management.Automation.ErrorRecord]) के रूप में कैप्चर किया जाता है , स्ट्रिंग्स को नहीं, इसलिए आउटपुट एरे में स्ट्रिंग्स का मिश्रण (प्रत्येक स्ट्रिंग एक stdout लाइन का प्रतिनिधित्व करता है) और एरर रिकॉर्ड्स हो सकते हैं (प्रत्येक रिकॉर्ड एक stderr लाइन का प्रतिनिधित्व करते हैं) । ध्यान दें कि, जैसा कि अनुरोध किया गया है 2>&1, दोनों स्ट्रिंग्स और त्रुटि रिकॉर्ड पॉवरशेल की सफलता आउटपुट स्ट्रीम के माध्यम से प्राप्त किए जाते हैं )।

      • कंसोल में, त्रुटि रिकॉर्ड लाल रंग में प्रिंट होती है , और डिफ़ॉल्ट रूप से 1 एक बहु-रेखा प्रदर्शन का उत्पादन करता है , उसी प्रारूप में जो कि cmdlet की गैर-समाप्ति त्रुटि प्रदर्शित करेगा; बाद में त्रुटि रिकॉर्ड लाल रंग में भी प्रिंट होता है, लेकिन केवल एक लाइन पर उनके त्रुटि संदेश को प्रिंट करते हैं ।

      • जब outputting कंसोल के लिए , तार आम तौर पर आने के पहले उत्पादन सरणी में, ( "एक ही समय में" stdout / stderr लाइनों उत्पादन का एक बैच के बीच कम से कम) त्रुटि रिकॉर्ड द्वारा पीछा किया, लेकिन, सौभाग्य से, जब आप पर कब्जा उत्पादन , यह ठीक से interleaved है , उसी आउटपुट ऑर्डर का उपयोग करके जिसे आप बिना प्राप्त करेंगे 2>&1; दूसरे शब्दों में: जब करने के लिए outputting कंसोल , पर कब्जा कर लिया उत्पादन जिस क्रम में stdout और stderr लाइनों बाहरी कमांड द्वारा उत्पन्न किया गया प्रतिबिंबित नहीं करता।

      • यदि आप एक में पूरे उत्पादन पर कब्जा एकल के साथ स्ट्रिंगOut-String , PowerShell जोड़ देगा अतिरिक्त लाइनें , क्योंकि एक त्रुटि रिकॉर्ड की स्ट्रिंग प्रतिनिधित्व ऐसे स्थान (के रूप में अतिरिक्त जानकारी शामिल है At line:...) और श्रेणी ( + CategoryInfo ...); उत्सुकता से, यह केवल पहली त्रुटि रिकॉर्ड पर लागू होता है ।

        • इस समस्या को हल करने के लिए, लागू .ToString()करने के बजाय करने के लिए पाइप में से प्रत्येक के उत्पादन वस्तु के लिए विधि Out-String:
          $cmdOutput = <command> 2>&1 | % { $_.ToString() };
          PS v3 + में आप सरल कर सकते हैं:
          $cmdOutput = <command> 2>&1 | % ToString
          (एक बोनस के रूप में, यदि आउटपुट कैप्चर नहीं किया गया है, तो यह कंसोल पर प्रिंटिंग के समय भी ठीक से इंटरलेय्ड आउटपुट तैयार करता है।)
      • वैकल्पिक रूप से, त्रुटि रिकॉर्ड को फ़िल्टर बाहर और साथ PowerShell की त्रुटि धारा में उन्हें भेजने केWrite-Error (एक बोनस के रूप में, यदि उत्पादन पर कब्जा कर लिया है, यह ठीक से interleaved उत्पादन भी जब कंसोल पर प्रिंट का उत्पादन):

$cmdOutput = <command> 2>&1 | ForEach-Object {
  if ($_ -is [System.Management.Automation.ErrorRecord]) {
    Write-Error $_
  } else {
    $_
  }
}

जब मैंने अपने निष्पादन योग्य मार्ग और इसके लिए अपनी दलीलें लीं, तो उन्हें अंततः मेरे लिए काम किया और उन्हें एक स्ट्रिंग में फेंक दिया और मेरे <कमांड> के रूप में व्यवहार किया।
दान

2
@Dan: जब PowerShell खुद की व्याख्या करता है <command>, तो आपको निष्पादन योग्य और उसके तर्कों को एक ही स्ट्रिंग में संयोजित नहीं करना चाहिए ; cmd /cआपके माध्यम से मंगलाचरण के साथ ऐसा हो सकता है, और यह स्थिति पर निर्भर करता है कि यह समझ में आता है या नहीं। आप किस परिदृश्य का उल्लेख कर रहे हैं, और क्या आप एक न्यूनतम उदाहरण दे सकते हैं?
mklement0

काम करता है: $ कमांड = "c: \ mycommand.exe" + $ Args ..... $ आउटपुट = cmd / c $ कमांड '2> & 1'
दान

1
@ दान: हाँ, यह काम करता है, हालांकि आपको +ऑपरेटर के साथ स्ट्रिंग के मध्यवर्ती चर और स्पष्ट निर्माण की आवश्यकता नहीं है ; निम्नलिखित कार्य भी: cmd /c c:\mycommand.exe $Args '2>&1'- PowerShell $Argsउस मामले में एक अंतरिक्ष-पृथक स्ट्रिंग के रूप में तत्वों को पारित करने का ध्यान रखता है , एक विशेषता जिसे स्प्लैशिंग कहा जाता है ।
15:27 पर mklement0

अंत में एक उचित उत्तर जो PS6.1 + में काम करता है। सॉस में रहस्य वास्तव में '2>&1'हिस्सा है, और ()कई स्क्रिप्ट में संलग्न नहीं है।
not2qubit

24

यदि आप त्रुटि आउटपुट को पुनर्निर्देशित करना चाहते हैं, तो आपको करना होगा:

$cmdOutput = command 2>&1

या, यदि प्रोग्राम के नाम में रिक्त स्थान हैं:

$cmdOutput = & "command with spaces" 2>&1

4
2> और 1 का क्या अर्थ है? 'रन कमांड 2 कहा जाता है और अपने आउटपुट को रन कमांड में कहते हैं'?
रिचर्ड

7
इसका अर्थ है "मानक त्रुटि आउटपुट (फ़ाइल डिस्क्रिप्टर 2) को उसी स्थान पर रीडायरेक्ट करें जहां मानक आउटपुट (फाइल डिस्क्रिप्टर 1) जा रहा है"। मूल रूप से, एक ही स्थान पर सामान्य और त्रुटि संदेशों को रीडायरेक्ट करता है (इस मामले में कंसोल, यदि स्टडआउट को कहीं और रीडायरेक्ट नहीं किया गया है - फ़ाइल की तरह)।
गियोवन्नी तिरलोनी

11

या यह प्रयास करें। यह आउटपुट को चर $ scriptOutput में कैप्चर करेगा:

& "netdom.exe" $params | Tee-Object -Variable scriptOutput | Out-Null

$scriptOutput

7
-1, अनावश्यक रूप से जटिल। $scriptOutput = & "netdom.exe" $params
चार्ल्सबी

8
आउट-नल को हटाना और यह एक ही समय में शेल और एक चर दोनों के लिए पाइपिंग के लिए बहुत अच्छा है।
ferventcoder

10

एक और वास्तविक जीवन उदाहरण:

$result = & "$env:cust_tls_store\Tools\WDK\x64\devcon.exe" enable $strHwid 2>&1 | Out-String

ध्यान दें कि इस उदाहरण में एक पथ शामिल है (जो एक पर्यावरण चर के साथ शुरू होता है)। ध्यान दें कि उद्धरण पथ और EXE फ़ाइल को घेरना चाहिए, लेकिन पैरामीटर नहीं!

नोट:& कमांड के सामने चरित्र को मत भूलना , लेकिन उद्धरणों के बाहर।

त्रुटि आउटपुट भी एकत्र किया गया है।

इस संयोजन को काम करने में मुझे थोड़ा समय लगा, इसलिए मैंने सोचा कि मैं इसे साझा करूंगा।


8

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

मेरे साथ मिला कच्चा परिणाम:

$rawOutput = (cmd /c <command> 2`>`&1)

2

मुझे काम करने के लिए निम्नलिखित मिला:

$Command1="C:\\ProgramData\Amazon\Tools\ebsnvme-id.exe"
$result = & invoke-Expression $Command1 | Out-String

$ परिणाम आपको जरूरतमंद देता है



0

यदि आप सभी करने की कोशिश कर रहे हैं तो आउटपुट को कमांड से कैप्चर करें, तो यह अच्छी तरह से काम करेगा।

मैं इसे सिस्टम समय बदलने के लिए उपयोग करता हूं, जैसा [timezoneinfo]::localकि आप सिस्टम में परिवर्तन करने के बाद भी हमेशा एक ही जानकारी का उत्पादन करते हैं । यह एकमात्र तरीका है जिसे मैं समय क्षेत्र में परिवर्तन को मान्य और लॉग इन कर सकता हूं:

$NewTime = (powershell.exe -command [timezoneinfo]::local)
$NewTime | Tee-Object -FilePath $strLFpath\$strLFName -Append

मतलब कि मुझे सिस्टम चर को फिर से लोड करने के लिए एक नया पॉवरशेल सत्र खोलना होगा ।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.