หมายเหตุ: คำสั่งในคำถามใช้Start-Process
ซึ่งป้องกันการดักจับผลลัพธ์ของโปรแกรมเป้าหมายโดยตรง โดยทั่วไป อย่าใช้Start-Process
เพื่อเรียกใช้แอปพลิเคชันคอนโซลแบบซิงโครนัส - เพียงเรียกใช้โดยตรงเช่นเดียวกับเชลล์ การทำเช่นนี้จะทำให้แอปพลิเคชันเชื่อมต่อกับสตรีมมาตรฐานของคอนโซลการโทรทำให้สามารถบันทึกเอาต์พุตได้โดยการมอบหมายอย่างง่าย$output = netdom ...
ดังรายละเอียดด้านล่าง
โดยพื้นฐานแล้วการจับเอาท์พุทจากยูทิลิตี้ภายนอกทำงานเหมือนกับคำสั่ง PowerShell-native (คุณอาจต้องการทบทวนวิธีการใช้งานเครื่องมือภายนอก ):
$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 นี้เพื่อการสนทนาเกี่ยวกับความคลาดเคลื่อนนี้
ในการดักจับเอาต์พุตจากหลายคำสั่งให้ใช้ subexpression ( $(...)
) หรือเรียกบล็อกสคริปต์ ( { ... }
) ด้วย&
หรือ.
:
$cmdOutput = $(<command>; ...) # subexpression
$cmdOutput = & {<command>; ...} # script block with & - creates child scope for vars.
$cmdOutput = . {<command>; ...} # script block with . - no child scope
โปรดทราบว่าจำเป็นที่จะต้องทั่วไปคำนำหน้าด้วย&
(ผู้ประกอบการโทร) คำสั่งของแต่ละบุคคลที่มีชื่อ / เส้นทางที่ยกมา - เช่น$cmdOutput = & 'netdom.exe' ...
- ไม่เกี่ยวข้องกับโปรแกรมภายนอกต่อ se (มันเท่า ๆ กันนำไปใช้กับสคริปต์ PowerShell) แต่เป็นไวยากรณ์ต้องการ : PowerShell แยกวิเคราะห์คำสั่งที่เริ่มต้นด้วยสตริงที่ยกมาในโหมดการแสดงออกโดยค่าเริ่มต้นในขณะที่โหมดอาร์กิวเมนต์ที่จำเป็นในการเรียกคำสั่ง (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.
ข้อควรพิจารณาเฉพาะสำหรับโปรแกรมภายนอก :
โปรแกรมภายนอกเนื่องจากทำงานนอกระบบชนิดของ PowerShell จะส่งคืนสตริงผ่านสตรีมสำเร็จเท่านั้น (stdout)
หากการส่งออกมีมากกว่า 1 เส้น , PowerShell โดยค่าเริ่มต้นแยกลงในอาร์เรย์ของสตริง แม่นยำยิ่งขึ้นบรรทัดเอาต์พุตจะถูกเก็บไว้ในอาร์เรย์ประเภท[System.Object[]]
ที่มีองค์ประกอบเป็นสตริง ( [System.String]
)
หากคุณต้องการส่งออกจะเป็นคนเดียวที่อาจเกิดขึ้นหลายสายสตริง , ท่อOut-String
:
$cmdOutput = <command> | Out-String
การเปลี่ยนเส้นทาง stderr ไปยัง stdout ด้วย2>&1
เช่นกันเพื่อจับภาพไว้เป็นส่วนหนึ่งของสตรีมความสำเร็จมาพร้อมกับcaveats :
ที่จะทำให้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 /c
เรียกใช้cmd.exe
ด้วยคำสั่ง<command>
และออกหลังจาก<command>
เสร็จสิ้น
- สังเกตเครื่องหมายอัญประกาศเดี่ยว
2>&1
ซึ่งรับประกันว่าการเปลี่ยนเส้นทางจะถูกส่งไปยังcmd.exe
แทนที่จะถูกตีความโดย PowerShell
โปรดทราบว่าเกี่ยวข้องกับcmd.exe
หมายความว่าของกฎระเบียบสำหรับการหลบหนีตัวอักษรและการขยายตัวแปรสภาพแวดล้อมเข้ามาเล่นโดยเริ่มต้นในนอกเหนือไปจากความต้องการของตัวเอง PowerShell ของ; ใน PS v3 + คุณสามารถใช้พารามิเตอร์พิเศษ--%
(ที่เรียกว่าสัญลักษณ์หยุดแยก ) เพื่อปิดการตีความของพารามิเตอร์ที่เหลือโดย PowerShell ยกเว้นสำหรับการอ้างอิงตัวแปรสภาพแวดล้อมสไตล์เช่นcmd.exe
%PATH%
โปรดทราบว่าเนื่องจากคุณรวม stdout และ stderr ที่แหล่งที่มาด้วยวิธีนี้คุณจะไม่สามารถแยกความแตกต่างระหว่างบรรทัด stdout-originated และ stderr-origin กำเนิดใน PowerShell; หากคุณต้องการความแตกต่างนี้ให้ใช้การ2>&1
เปลี่ยนเส้นทางของ PowerShell - ดูด้านล่าง
ใช้การ 2>&1
เปลี่ยนเส้นทางของ PowerShellเพื่อทราบว่าสายใดมาจากสตรีมใด :
stderrเอาท์พุทถูกจับเป็นบันทึกข้อผิดพลาด ( [System.Management.Automation.ErrorRecord]
) ไม่ใช่สตริงดังนั้นอาร์เรย์การส่งออกอาจมีการผสมของสตริง (แต่ละสายเป็นตัวแทนของสาย stdout) และบันทึกข้อผิดพลาด (แต่ละระเบียนที่เป็นตัวแทนของสาย stderr ก) โปรดทราบว่าตามที่ร้องขอโดย2>&1
ทั้งสตริงและบันทึกข้อผิดพลาดจะได้รับผ่านสตรีมเอาต์พุตความสำเร็จของ PowerShell
ในคอนโซลบันทึกข้อผิดพลาดจะพิมพ์เป็นสีแดงและรายการที่1โดยค่าเริ่มต้นจะสร้างการแสดงผลหลายบรรทัดในรูปแบบเดียวกับที่ข้อผิดพลาดที่ไม่สิ้นสุดของ cmdlet จะแสดงขึ้น ภายหลังบันทึกข้อผิดพลาดพิมพ์สีแดงเช่นกัน แต่พิมพ์เฉพาะข้อผิดพลาดของพวกเขาข้อความบนบรรทัดเดียว
เมื่อส่งออกไปยังคอนโซลสตริงมักจะมาก่อนในอาร์เรย์อาร์เรย์ตามด้วยบันทึกข้อผิดพลาด (อย่างน้อยในกลุ่มของ stdout / stderr บรรทัดเอาท์พุท "ในเวลาเดียวกัน") แต่โชคดีเมื่อคุณจับเอาท์พุท มันเป็น interleaved อย่างถูกต้องโดยใช้คำสั่งเอาท์พุทเดียวกับที่คุณจะได้รับโดยไม่ต้อง2>&1
; กล่าวอีกนัยหนึ่ง: เมื่อแสดงผลไปยังคอนโซลเอาต์พุตที่ดักจับจะไม่แสดงลำดับที่บรรทัด 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
(เป็นโบนัสหากไม่ได้จับเอาท์พุทสิ่งนี้จะสร้างเอาต์พุตแบบแทรกอย่างถูกต้องแม้เมื่อพิมพ์ไปยังคอนโซล):
$cmdOutput = <command> 2>&1 | ForEach-Object {
if ($_ -is [System.Management.Automation.ErrorRecord]) {
Write-Error $_
} else {
$_
}
}
Start-Process
เพื่อเรียกใช้แอปพลิเคชันคอนโซล (ตามคำจำกัดความภายนอก) แบบซิงโครนัส - เพียงเรียกใช้โดยตรงในเชลล์ใด ๆ เพื่อปัญญา:netdom /verify $pc /domain:hosp.uhhg.org
.$output = netdom ...
การทำเช่นนี้จะช่วยให้แอพลิเคชันที่เชื่อมต่อกับลำธารมาตรฐานคอนโซลโทรที่ช่วยให้การส่งออกของมันจะถูกจับโดยการโอนง่าย คำตอบส่วนใหญ่ที่ระบุด้านล่างโดยนัยละเลยStart-Process
ในความโปรดปรานของการดำเนินการโดยตรง