บันทึก:
วิธีการแก้ปัญหาต่อไปนี้ทำงานร่วมกับใด ๆโปรแกรมภายนอกและจับการส่งออกอย่างสม่ำเสมอเป็นข้อความ
การเรียกใช้อินสแตนซ์ PowerShell อีกและจับเอาท์พุทเป็นวัตถุที่อุดมไปด้วย (ที่มีข้อ จำกัด ) ดูวิธีการแก้ปัญหาที่แตกต่างกันในส่วนด้านล่างหรือพิจารณาคำตอบที่เป็นประโยชน์งัดอาร์เซ่นส์ซึ่งใช้PowerShell SDK
ต่อไปนี้เป็นแนวคิดที่พิสูจน์จากการใช้โดยตรงSystem.Diagnostics.Process
และSystem.Diagnostics.ProcessStartInfo
ประเภท. NET เพื่อจับผลลัพธ์กระบวนการในหน่วยความจำ (ตามที่ระบุไว้ในคำถามของคุณStart-Process
ไม่ใช่ตัวเลือกเพราะรองรับเฉพาะการบันทึกผลลัพธ์ในไฟล์ดังที่แสดงในคำตอบนี้ ) :
บันทึก:
เนื่องจากการทำงานในฐานะผู้ใช้ที่แตกต่างกันสิ่งนี้ได้รับการรองรับบนWindows เท่านั้น (จาก. NET Core 3.1) แต่ในทั้งสองรุ่น PowerShell
เนื่องจากจำเป็นต้องเรียกใช้ในฐานะผู้ใช้ที่แตกต่างกันและต้องการจับเอาท์พุท.WindowStyle
จึงไม่สามารถใช้เพื่อเรียกใช้คำสั่งที่ซ่อนอยู่ได้ (เนื่องจากการใช้.WindowStyle
ต้อง.UseShellExecute
เป็น$true
ซึ่งไม่สอดคล้องกับข้อกำหนดเหล่านี้) อย่างไรก็ตามเนื่องจากเอาต์พุตทั้งหมดจะถูกดักจับการตั้งค่า.CreateNoNewWindow
ให้$true
ผลลัพธ์อย่างมีประสิทธิภาพในการประมวลผลที่ซ่อนอยู่
# Get the target user's name and password.
$cred = Get-Credential
# Create a ProcessStartInfo instance
# with the relevant properties.
$psi = [System.Diagnostics.ProcessStartInfo] @{
# For demo purposes, use a simple `cmd.exe` command that echoes the username.
# See the bottom section for a call to `powershell.exe`.
FileName = 'cmd.exe'
Arguments = '/c echo %USERNAME%'
# Set this to a directory that the target user
# is permitted to access.
WorkingDirectory = 'C:\' #'
# Ask that output be captured in the
# .StandardOutput / .StandardError properties of
# the Process object created later.
UseShellExecute = $false # must be $false
RedirectStandardOutput = $true
RedirectStandardError = $true
# Uncomment this line if you want the process to run effectively hidden.
# CreateNoNewWindow = $true
# Specify the user identity.
# Note: If you specify a UPN in .UserName
# (user@doamin.com), set .Domain to $null
Domain = $env:USERDOMAIN
UserName = $cred.UserName
Password = $cred.Password
}
# Create (launch) the process...
$ps = [System.Diagnostics.Process]::Start($psi)
# Read the captured standard output.
# By reading to the *end*, this implicitly waits for (near) termination
# of the process.
# Do NOT use $ps.WaitForExit() first, as that can result in a deadlock.
$stdout = $ps.StandardOutput.ReadToEnd()
# Uncomment the following lines to report the process' exit code.
# $ps.WaitForExit()
# "Process exit code: $($ps.ExitCode)"
"Running ``cmd /c echo %USERNAME%`` as user $($cred.UserName) yielded:"
$stdout
ผลลัพธ์ข้างต้นให้ผลดังนี้: แสดงว่ากระบวนการรันด้วยข้อมูลประจำตัวผู้ใช้ที่กำหนดสำเร็จแล้ว:
Running `cmd /c echo %USERNAME%` as user jdoe yielded:
jdoe
เนื่องจากคุณกำลังเรียกใช้อินสแตนซ์PowerShellอื่นคุณอาจต้องการใช้ประโยชน์จากความสามารถของPowerShell CLIในการแสดงผลลัพธ์ในรูปแบบ CLIXML ซึ่งช่วยให้การแยกสัญญาณออกเป็นวัตถุที่มีความอุดมสมบูรณ์แม้ว่าจะมีข้อ จำกัด ประเภทที่ถูกต้องตามที่อธิบายไว้ในคำตอบที่เกี่ยวข้องนี้ .
# Get the target user's name and password.
$cred = Get-Credential
# Create a ProcessStartInfo instance
# with the relevant properties.
$psi = [System.Diagnostics.ProcessStartInfo] @{
# Invoke the PowerShell CLI with a simple sample command
# that calls `Get-Date` to output the current date as a [datetime] instance.
FileName = 'powershell.exe'
# `-of xml` asks that the output be returned as CLIXML,
# a serialization format that allows deserialization into
# rich objects.
Arguments = '-of xml -noprofile -c Get-Date'
# Set this to a directory that the target user
# is permitted to access.
WorkingDirectory = 'C:\' #'
# Ask that output be captured in the
# .StandardOutput / .StandardError properties of
# the Process object created later.
UseShellExecute = $false # must be $false
RedirectStandardOutput = $true
RedirectStandardError = $true
# Uncomment this line if you want the process to run effectively hidden.
# CreateNoNewWindow = $true
# Specify the user identity.
# Note: If you specify a UPN in .UserName
# (user@doamin.com), set .Domain to $null
Domain = $env:USERDOMAIN
UserName = $cred.UserName
Password = $cred.Password
}
# Create (launch) the process...
$ps = [System.Diagnostics.Process]::Start($psi)
# Read the captured standard output, in CLIXML format,
# stripping the `#` comment line at the top (`#< CLIXML`)
# which the deserializer doesn't know how to handle.
$stdoutCliXml = $ps.StandardOutput.ReadToEnd() -replace '^#.*\r?\n'
# Uncomment the following lines to report the process' exit code.
# $ps.WaitForExit()
# "Process exit code: $($ps.ExitCode)"
# Use PowerShell's deserialization API to
# "rehydrate" the objects.
$stdoutObjects = [Management.Automation.PSSerializer]::Deserialize($stdoutCliXml)
"Running ``Get-Date`` as user $($cred.UserName) yielded:"
$stdoutObjects
"`nas data type:"
$stdoutObjects.GetType().FullName
ผลลัพธ์ข้างต้นแสดงผลดังนี้: แสดงว่า[datetime]
อินสแตนซ์ ( System.DateTime
) เอาต์พุตโดยGet-Date
ถูกยกเลิกการเรียงลำดับเช่น:
Running `Get-Date` as user jdoe yielded:
Friday, March 27, 2020 6:26:49 PM
as data type:
System.DateTime