catatan:
Solusi berikut ini berfungsi dengan program eksternal apa pun , dan menangkap output sebagai teks .
Untuk memanggil instance PowerShell lain dan menangkap outputnya sebagai objek kaya (dengan keterbatasan), lihat solusi varian di bagian bawah atau pertimbangkan jawaban Mathias R. Jessen yang bermanfaat , yang menggunakan PowerShell SDK .
Berikut adalah bukti konsep yang didasarkan pada penggunaan langsung tipe System.Diagnostics.Process
dan System.Diagnostics.ProcessStartInfo
.NET untuk menangkap output proses dalam memori (sebagaimana dinyatakan dalam pertanyaan Anda, Start-Process
bukan opsi, karena hanya mendukung menangkap keluaran dalam file , seperti yang ditunjukkan dalam jawaban ini ) :
catatan:
Karena berjalan sebagai pengguna yang berbeda, ini hanya didukung pada Windows (pada .NET Core 3.1), tetapi di kedua edisi PowerShell di sana.
Karena perlu dijalankan sebagai user yang berbeda dan perlu untuk menangkap output, .WindowStyle
tidak dapat digunakan untuk menjalankan perintah tersembunyi (karena menggunakan .WindowStyle
membutuhkan .UseShellExecute
untuk menjadi $true
, yang tidak sesuai dengan persyaratan ini); Namun, karena semua output ditangkap , pengaturan .CreateNoNewWindow
untuk $true
secara efektif menghasilkan eksekusi tersembunyi.
# 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
Di atas menghasilkan sesuatu seperti berikut, menunjukkan bahwa proses berhasil berjalan dengan identitas pengguna yang diberikan:
Running `cmd /c echo %USERNAME%` as user jdoe yielded:
jdoe
Karena Anda memanggil instance PowerShell lain , Anda mungkin ingin memanfaatkan kemampuan PowerShell CLI untuk merepresentasikan output dalam format CLIXML, yang memungkinkan deserialisasi output menjadi objek kaya , meskipun dengan ketelitian jenis terbatas , seperti dijelaskan dalam jawaban terkait ini .
# 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
Output di atas menghasilkan sesuatu seperti yang berikut, menunjukkan bahwa [datetime]
instance ( System.DateTime
) output oleh Get-Date
deserialized seperti:
Running `Get-Date` as user jdoe yielded:
Friday, March 27, 2020 6:26:49 PM
as data type:
System.DateTime