시작 프로세스에서 표준 출력 및 오류 캡처
PowerShell에 버그가 있습니까?Start-Process액세스 시 명령어StandardError그리고.StandardOutput프로퍼티?
다음을 실행하면 출력이 표시되지 않습니다.
$process = Start-Process -FilePath ping -ArgumentList localhost -NoNewWindow -PassThru -Wait
$process.StandardOutput
$process.StandardError
그러나 출력을 파일로 리디렉션하면 예상된 결과를 얻을 수 있습니다.
$process = Start-Process -FilePath ping -ArgumentList localhost -NoNewWindow -PassThru -Wait -RedirectStandardOutput stdout.txt -RedirectStandardError stderr.txt
어떤 이유에선지 그렇게 설계되었죠파일로 전송하지 않고 얻을 수 있는 방법은 다음과 같습니다.
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = "ping.exe"
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.Arguments = "localhost"
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
$p.WaitForExit()
$stdout = $p.StandardOutput.ReadToEnd()
$stderr = $p.StandardError.ReadToEnd()
Write-Host "stdout: $stdout"
Write-Host "stderr: $stderr"
Write-Host "exit code: " + $p.ExitCode
질문의 코드는, 개시 변수의 ExitCode 속성을 읽어낼 수 있다고 생각합니다.
$process = Start-Process -FilePath ping -ArgumentList localhost -NoNewWindow -PassThru -Wait
$process.ExitCode
(예시와 같이) 를 추가할 필요가 있습니다.-PassThru그리고.-Wait파라미터(이 때문에 잠시 당황했습니다)
중요:
우리는 LPG에서 제공하는 기능을 사용해 왔습니다.
그러나 여기에는 많은 출력을 생성하는 프로세스를 시작할 때 발생할 수 있는 버그가 포함되어 있습니다.이로 인해 이 기능을 사용할 때 교착 상태가 발생할 수 있습니다.대신 다음과 같은 적응 버전을 사용합니다.
Function Execute-Command ($commandTitle, $commandPath, $commandArguments)
{
Try {
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = $commandPath
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.Arguments = $commandArguments
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
[pscustomobject]@{
commandTitle = $commandTitle
stdout = $p.StandardOutput.ReadToEnd()
stderr = $p.StandardError.ReadToEnd()
ExitCode = $p.ExitCode
}
$p.WaitForExit()
}
Catch {
exit
}
}
이 문제에 대한 자세한 내용은 MSDN에서 확인할 수 있습니다.
부모 프로세스가 p를 호출하면 교착 상태가 발생할 수 있습니다.p 전에 Wait For Exit.Standard Error(표준 에러)ReadToEnd 및 자식 프로세스는 리디렉션된 스트림을 채우기에 충분한 텍스트를 씁니다.부모 프로세스는 자식 프로세스가 종료될 때까지 무기한 대기합니다.하위 프로세스는 부모가 StandardError 스트림 전체를 읽을 때까지 무기한 대기합니다.
저도 이런 문제가 있어서 Andy의 코드를 사용하여 여러 명령어를 실행해야 할 때 정리하는 기능을 만들었습니다.
stderr, stdout 및 exit 코드를 객체로 반환합니다.주의사항: 함수가 받아들이지 않음.\전체 경로를 사용해야 합니다.
Function Execute-Command ($commandTitle, $commandPath, $commandArguments)
{
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = $commandPath
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.Arguments = $commandArguments
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
$p.WaitForExit()
[pscustomobject]@{
commandTitle = $commandTitle
stdout = $p.StandardOutput.ReadToEnd()
stderr = $p.StandardError.ReadToEnd()
ExitCode = $p.ExitCode
}
}
사용 방법은 다음과 같습니다.
$DisableACMonitorTimeOut = Execute-Command -commandTitle "Disable Monitor Timeout" -commandPath "C:\Windows\System32\powercfg.exe" -commandArguments " -x monitor-timeout-ac 0"
Andy Arismendi와 LPG에서 나온 그 예들로 인해 나는 정말로 어려움을 겪었다.항상 다음을 사용해야 합니다.
$stdout = $p.StandardOutput.ReadToEnd()
부르기 전에
$p.WaitForExit()
완전한 예는 다음과 같습니다.
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = "ping.exe"
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.Arguments = "localhost"
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
$stdout = $p.StandardOutput.ReadToEnd()
$stderr = $p.StandardError.ReadToEnd()
$p.WaitForExit()
Write-Host "stdout: $stdout"
Write-Host "stderr: $stderr"
Write-Host "exit code: " + $p.ExitCode
다음은 다른 powershell 프로세스(시리얼화)에서 출력을 얻기 위한 복잡한 방법입니다.
start-process -wait -nonewwindow powershell 'ps | Export-Clixml out.xml'
import-clixml out.xml
강조하겠습니다.-nonewwindow표준 출력 및 표준 오류를 표시하려면 적어도 로컬 화면에서 다음을 수행합니다.
start-process -wait cmd '/c dir' -nonewwindow
Volume in drive C is Windows
Volume Serial Number is 2AC6-626F
Directory of C:\users\me\foo
11/24/2022 11:40 AM <DIR> .
11/24/2022 11:40 AM <DIR> ..
11/24/2022 11:40 AM 330 file.json
1 File(s) 330 bytes
2 Dir(s) 25,042,915,328 bytes free
start-process -wait cmd '/c dir foo' -nonewwindow
Volume in drive C is Windows
Volume Serial Number is 2AC6-626F
Directory of C:\users\me\foo
File Not Found
여기 다른 사람들이 이 스레드에 올린 예를 바탕으로 제가 만든 것이 있습니다.이 버전에서는 콘솔 창이 숨겨지고 출력 표시 옵션이 제공됩니다.
function Invoke-Process {
[CmdletBinding(SupportsShouldProcess)]
param
(
[Parameter(Mandatory)]
[ValidateNotNullOrEmpty()]
[string]$FilePath,
[Parameter()]
[ValidateNotNullOrEmpty()]
[string]$ArgumentList,
[ValidateSet("Full","StdOut","StdErr","ExitCode","None")]
[string]$DisplayLevel
)
$ErrorActionPreference = 'Stop'
try {
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = $FilePath
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.WindowStyle = 'Hidden'
$pinfo.CreateNoWindow = $true
$pinfo.Arguments = $ArgumentList
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
$result = [pscustomobject]@{
Title = ($MyInvocation.MyCommand).Name
Command = $FilePath
Arguments = $ArgumentList
StdOut = $p.StandardOutput.ReadToEnd()
StdErr = $p.StandardError.ReadToEnd()
ExitCode = $p.ExitCode
}
$p.WaitForExit()
if (-not([string]::IsNullOrEmpty($DisplayLevel))) {
switch($DisplayLevel) {
"Full" { return $result; break }
"StdOut" { return $result.StdOut; break }
"StdErr" { return $result.StdErr; break }
"ExitCode" { return $result.ExitCode; break }
}
}
}
catch {
exit
}
}
예:Invoke-Process -FilePath "FQPN" -ArgumentList "ARGS" -DisplayLevel Full
stdout과 stderr를 모두 얻으려면 다음을 사용합니다.
Function GetProgramOutput([string]$exe, [string]$arguments)
{
$process = New-Object -TypeName System.Diagnostics.Process
$process.StartInfo.FileName = $exe
$process.StartInfo.Arguments = $arguments
$process.StartInfo.UseShellExecute = $false
$process.StartInfo.RedirectStandardOutput = $true
$process.StartInfo.RedirectStandardError = $true
$process.Start()
$output = $process.StandardOutput.ReadToEnd()
$err = $process.StandardError.ReadToEnd()
$process.WaitForExit()
$output
$err
}
$exe = "cmd"
$arguments = '/c echo hello 1>&2' #this writes 'hello' to stderr
$runResult = (GetProgramOutput $exe $arguments)
$stdout = $runResult[-2]
$stderr = $runResult[-1]
[System.Console]::WriteLine("Standard out: " + $stdout)
[System.Console]::WriteLine("Standard error: " + $stderr)
답변 개선 - 고객님이 괜찮으시다면Start-JobStart-Process
와 는, STDOUT 「STDERR」라고 하는 되어 있는 되었습니다.$job.ChildJobs[0].Output ★★★★★★★★★★★★★★★★★」$job.ChildJobs[0].Error스크립트가 실행될 때.따라서 이러한 값을 폴링하여 정기적으로 작성할 수 있습니다.★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
그러나 스트림이 아니므로 수동으로 시작 인덱스를 배열에 추적해야 합니다.
, 를 STDOUT로 표시합니다.$job.ChildJobs[0].Output그리고 이 데모에서는 약간의 보너스로 호출 스크립트는 PS7, 백그라운드 작업은 PS5입니다.
$scriptBlock = {
Param ([int]$param1, [int]$param2)
$PSVersionTable
Start-Sleep -Seconds 1
$param1 + $param2
}
$parameters = @{
ScriptBlock = $scriptBlock
ArgumentList = 1, 2
PSVersion = 5.1 # <-- remove this line for PS7
}
$timeoutSec = 5
$job = Start-Job @parameters
$job.ChildJobs[0].Output
$index = $job.ChildJobs[0].Output.Count
while ($job.JobStateInfo.State -eq [System.Management.Automation.JobState]::Running) {
Start-Sleep -Milliseconds 200
$job.ChildJobs[0].Output[$index]
$index = $job.ChildJobs[0].Output.Count
if (([DateTime]::Now - $job.PSBeginTime).TotalSeconds -gt $timeoutSec) {
throw "Job timed out."
}
}
지적하신 바와 같이 저의 원래 답변은 출력을 인터리브할 수 있습니다.이는 PowerShell에서의 이벤트 처리 제한 사항입니다.그것은 고칠 수 있는 문제가 아니다.
원래 답변은 사용하지 마십시오.관심을 위해 여기에 남겨둡니다.
, 「 」ReadToEnd()선택사항이 아닙니다.고급 루프를 수행할 수 있지만 IMO를 사용하는 가장 깨끗한 방법은 스트림을 무시하는 것입니다.을 OutputDataReceived/ErrorDataReceived★★★★★★★★★★★★★★★★★★★★★★★★★★★★★이 접근방식을 통해 다른 사용자가 언급한 스레드화 문제도 회피할 수 있습니다.
Powershell을 사용하다히,는add_OutputDataReceived 적어도 PowerShell 51에서는 인 것 .PowerShell 5.1을 참조하십시오.)를 해결하려면 을 합니다.Register-ObjectEvent.
$stdout = New-Object System.Text.StringBuilder
$stderr = New-Object System.Text.StringBuilder
$proc = [System.Diagnostics.Process]@{
StartInfo = @{
FileName = 'ping.exe'
Arguments = 'google.com'
RedirectStandardOutput = $true
RedirectStandardError = $true
UseShellExecute = $false
WorkingDirectory = $PSScriptRoot
}
}
$stdoutEvent = Register-ObjectEvent $proc -EventName OutputDataReceived -MessageData $stdout -Action {
$Event.MessageData.AppendLine($Event.SourceEventArgs.Data)
}
$stderrEvent = Register-ObjectEvent $proc -EventName ErrorDataReceived -MessageData $stderr -Action {
$Event.MessageData.AppendLine($Event.SourceEventArgs.Data)
}
$proc.Start() | Out-Null
$proc.BeginOutputReadLine()
$proc.BeginErrorReadLine()
Wait-Process -Id $proc.Id -TimeoutSec 5
if ($proc.HasExited) {
$exitCode = $proc.ExitCode
}
else {
Stop-Process -Force -Id $proc.Id
$exitCode = -1
}
# Be sure to unregister. You have been warned.
Unregister-Event $stdoutEvent.Id
Unregister-Event $stderrEvent.Id
Write-Output $stdout.ToString()
Write-Output $stderr.ToString()
Write-Output "Exit code: $exitCode"
- 표시된 코드는 해피 패스입니다(stderr은 비어 있습니다).
- 패스를 하려면 , 「」를 합니다.
-TimeoutSec로로 합니다..5 - path를하려면 sad path(stderr)를 합니다.
FileName로로 합니다.'cmd'★★★★★★★★★★★★★★★★★」Arguments로로 합니다./C asdf
표준 시스템을 반환하는 내 버전의 기능은 다음과 같습니다.진단.3가지 새로운 속성으로 처리
Function Execute-Command ($commandTitle, $commandPath, $commandArguments)
{
Try {
$pinfo = New-Object System.Diagnostics.ProcessStartInfo
$pinfo.FileName = $commandPath
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.UseShellExecute = $false
$pinfo.WindowStyle = 'Hidden'
$pinfo.CreateNoWindow = $True
$pinfo.Arguments = $commandArguments
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.Start() | Out-Null
$stdout = $p.StandardOutput.ReadToEnd()
$stderr = $p.StandardError.ReadToEnd()
$p.WaitForExit()
$p | Add-Member "commandTitle" $commandTitle
$p | Add-Member "stdout" $stdout
$p | Add-Member "stderr" $stderr
}
Catch {
}
$p
}
& ""와 하는 것도 .--%시작 프로세스 대신 - 명령 및/또는 오류 출력을 쉽게 파이핑 및 처리할 수 있습니다.
- 이스케이프 파라미터를 변수에 입력하다
- 의론을 변수로 하다
$deploy= "C:\Program Files\IIS\Microsoft Web Deploy V3\msdeploy.exe"
$esc = '--%'
$arguments ="-source:package='c:\temp\pkg.zip' -verb:sync"
$output = & $deploy $esc $arguments
그러면 매개 변수가 간섭 없이 실행 파일에 전달되고 시작 프로세스의 문제를 해결할 수 있습니다.
Stderr와 Stdout을 하나의 변수로 결합합니다.
$output = & $deploy $esc $arguments 2>&1
Stderr 및 Stdout에 대한 개별 변수 가져오기
$err = $( $output = & $deploy $esc $arguments) 2>&1
언급URL : https://stackoverflow.com/questions/8761888/capturing-standard-out-and-error-with-start-process
'programing' 카테고리의 다른 글
| iOS에서 빠른 속도로 응용 프로그램의 화면 잠금을 방지하는 방법 (0) | 2023.04.10 |
|---|---|
| 배치 파일에서 PowerShell 스크립트를 실행하는 방법 (0) | 2023.04.10 |
| 특정 커밋용 Git 패치를 생성하려면 어떻게 해야 합니까? (0) | 2023.04.10 |
| 분기를 변경하지 않고 다른 Git 분기에 있는 파일 보기 (0) | 2023.04.10 |
| UILabel의 여러 줄 텍스트 (0) | 2023.04.10 |