$LastExitCode=0입니다만 $?= PowerShell에서 거짓.stdout으로 stderr을 리디렉션하면 Native CommandError가 발생함
PowerShell이 아래 두 번째 예제에서 놀라운 동작을 보이는 이유는 무엇입니까?
첫째, 제정신인 행동의 예:
PS C:\> & cmd /c "echo Hello from standard error 1>&2"; echo "`$LastExitCode=$LastExitCode and `$?=$?"
Hello from standard error
$LastExitCode=0 and $?=True
놀랄 일이 아닙니다.합니다.cmd
의echo
) 를 검사합니다.$?
그리고.$LastExitCode
이 값은 예상대로 각각 참 및 0과 같습니다.
그러나 PowerShell에 첫 번째 명령을 통해 표준 오류를 표준 출력으로 리디렉션하도록 요청하면 NativeCommandError:
PS C:\> & cmd /c "echo Hello from standard error 1>&2" 2>&1; echo "`$LastExitCode=$LastExitCode and `$?=$?"
cmd.exe : Hello from standard error
At line:1 char:4
+ cmd <<<< /c "echo Hello from standard error 1>&2" 2>&1; echo "`$LastExitCode=$LastExitCode and `$?=$?"
+ CategoryInfo : NotSpecified: (Hello from standard error :String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
$LastExitCode=0 and $?=False
첫 번째 질문입니다. Native Command Error가 발생한 이유는 무엇입니까?
왜 두번로, 는유이입니다.$?
때 cmd
성공적으로 실행되고,$LastExitCode
0입니까? 자동 변수에 대한 PowerShell의 문서에는 다음과 같은 기능이 명시적으로 정의되어 있지 않습니다.$?
나는 항상 그것이 진실이라고 생각했습니다 만약 그리고 오직 그렇다면.$LastExitCode
0이지만, 제 예는 그것과 모순됩니다.
이것이 제가 실제 세계에서 어떻게 이런 행동을 접하게 된 계기입니다(간체화).정말 FUBAR입니다.PowerShell 스크립트를 다른 스크립트에서 호출했습니다.내부 스크립트:
cmd /c "echo Hello from standard error 1>&2"
if (! $?)
{
echo "Job failed. Sending email.."
exit 1
}
# Do something else
이를 단순하게 실행하는 것은.\job.ps1
정상적으로 작동하며 이메일이 전송되지 않습니다.다른 에서 PowerShell의 PowerShell ..\job.ps1 2>&1 > log.txt
이 경우 이메일이 전송됩니다!오류 스트림을 사용하여 스크립트 외부에서 수행하는 작업은 스크립트의 내부 동작에 영향을 미칩니다.현상을 관찰하면 결과가 바뀝니다.이것은 스크립팅이라기 보다는 양자 물리학처럼 느껴집니다!
도:].\job.ps1 2>&1
도 있고 않을 수도 .
(PowerShell v2를 사용하고 있습니다.)
더'$?
는 변는에문있어다습에 나와 .about_Automatic_Variables
:
$?마지막 작업의 실행 상태를 포함합니다.
는 마지막 명령어인 PowerShell과 합니다. PowerShell은 PowerShell의 외부 명령어입니다.$LastExitCode
.
예에서, 를들어예는,$LastExitCode
0이었기 때문에 0입니다.cmd
일부 텍스트를 반향시키는 데 성공했습니다.그런데 그.2>&1
를 메지의원인으로 .stderr
출력 스트림의 오류 레코드로 변환됩니다. 이 레코드는 PowerShell에 마지막 작업 중에 오류가 발생하여 다음과 같은 문제가 발생했음을 알려줍니다.$?
되려고False
.
이를 좀 더 설명하기 위해 다음 사항을 고려합니다.
> java -jar foo; $?; $LastExitCodejarfile foo에 액세스할 수 없습니다.거짓의1
$LastExitCode
이는 java.exe의 종료 코드였기 때문에 1입니다.$?
셸이 마지막으로 한 일이 실패했기 때문에 거짓입니다.
하지만 내가 하는 일이 그것들을 바꾸는 것뿐이라면:
> java -jar foo; $LastExitCode; $? jarfile foo에 액세스할 수 없습니다.1 진실의
...그리고나서$?
셸이 마지막으로 한 일은 인쇄였기 때문에 사실입니다.$LastExitCode
호스트에게 성공적으로 전송이 성공적이었습니다.
마지막으로:
> &{java -jar foo}; $?; $LastExitCodejarfile foo에 액세스할 수 없습니다.진실의1
반대되는 ...약간 반대되는 것처럼 보이지만,$?
스크립트 블록 내부에서 실행되는 명령이 성공하지 못했더라도 스크립트 블록이 성공적으로 실행되었기 때문에 true입니다.
2>&1
리디렉션...출력 , 는 출력 스트림에 입니다.NativeCommandError
셸이 전체 오류 기록을 폐기하고 있습니다.
이것은 당신이 파이프만 하고 싶을 때 특히 짜증날 수 있습니다.stderr
그리고. stdout
로그 파일이나 다른 것으로 결합할 수 있도록 함께.PowerShell이 자신의 로그 파일에 삽입되기를 원하는 사람?내가 하면,ant build 2>&1 >build.log
그다에발모오류든는생하로 가는 .stderr
로그 파일에 오류 메시지를 확인하는 대신 PowerShell의 까다로운 $0.02를 추가했습니다.
그러나 출력 스트림은 텍스트 스트림이 아닙니다!리디렉션은 개체 파이프라인의 또 다른 구문일 뿐입니다.오류 레코드는 개체이므로 리디렉션하기 전에 해당 스트림의 개체를 문자열로 변환하기만 하면 됩니다.
시작:
> cmd /c "echo Hello from standard error 1>&2" 2>&1cmd.exe : 표준 오류에서 Hello라인:1 문자:4cmd &2" 2>&1카테고리정보: 지정되지 않음: (표준 오류:String에서 Hello) [], 원격 예외Fully Qualified ErrorId: 네이티브 명령 오류
받는 사람:
> cmd /c "echo Hello from standard error 1>&2" 2>&1 | %{ "$_" }표준 오류에서 Hello
...파일로 리디렉션하는 경우:
> cmd /c "echo Hello from standard error 1>&2" 2>&1 | %{"$_"} | 티아웃.txt표준 오류에서 Hello
...아니면 그냥:
> cmd /c "echo Hello from standard error 1>&2" 2>&1 | %{"$_"} > out.txt
이 버그는 오류 처리를 위한 PowerShell의 규범적인 설계의 예상치 못한 결과이므로 수정되지 않을 가능성이 높습니다.스크립트가 다른 PowerShell 스크립트에서만 재생되는 경우 안전합니다.그러나 스크립트가 광범위한 응용 프로그램과 상호 작용하면 이 버그가 발생할 수 있습니다.
PS> nslookup microsoft.com 2>&1 ; echo $?
False
알았어요! 그래도, 고통스럽게 긁고 나면, 교훈을 절대 잊지 못할 거예요.
사용하다($LastExitCode -eq 0)
에 $?
업데이트: v7.2에서 문제가 해결되었습니다. 이 답변을 참조하십시오.
v7.1 기준 문제 요약:
외부 프로그램 호출에 적용되는 리디렉션과 관련하여 PowerShell 엔진에 여전히 버그가 있습니다.
근본 원인은 를 사용하면 PowerShell의 오류 스트림(about_Redirection 참조)을 통해 stderr(표준 오류) 출력이 라우팅되므로 다음과 같은 바람직하지 않은 결과가 발생합니다.
한다면
$ErrorActionPreference = 'Stop'
실로적됨용, 사용을 사용하여 합니다.2>
예기치 않게 스크립트 종료 오류를 트리거합니다. 즉, 스크립트를 중단합니다(폼에서도 마찬가지).2>$null
여기서 stderr 라인을 무시하려는 의도가 분명합니다.)GitHub 이슈 #4002를 참조하십시오.- 방법: ( 설정하기: (임시) 설정하기
$ErrorActionPreference = 'Continue'
- 방법: ( 설정하기: (임시) 설정하기
때부터
2>
현재오스터치을합다니를 터치합니다.$?
자동성상변항상다음같설이다정니됩과수로 됩니다.$False
하나 이상의 stderr 라인이 방출되어 더 이상 명령의 실제 성공 상태를 반영하지 않는 경우.이 GitHub 문제를 참조하십시오.- 답변에서 권장하는 해결 방법: 항상 사용만 가능
$LASTEXITCODE -eq 0
외부 프로그램 호출 후 성공 여부를 테스트합니다.
- 답변에서 권장하는 해결 방법: 항상 사용만 가능
와 함께
2>
▁in▁recorded▁unex▁areder▁st▁st▁automatic다에 stderr 행이 예기치 않게 기록됩니다.$Error
variable(세션에서 발생한 모든 오류의 로그를 보관하는 변수) - 를 사용하는 경우에도2>$null
이 GitHub 문제를 참조하십시오.- 오류 : 추가되었는지 을 짧게 정리한 것은 오류 기록입니다.
$Error.RemoveAt()
하나씩, 하나도 없습니다.
- 오류 : 추가되었는지 을 짧게 정리한 것은 오류 기록입니다.
일반적으로 안타깝게도 일부 PowerShell 호스트는 기본적으로 PowerShell의 오류 스트림을 통해 외부 프로그램의 stderr 출력을 라우팅합니다. 즉, 많은 외부 프로그램이 상태 정보에 stderr을 사용하거나 더 일반적으로 데이터가 아닌 모든 항목에 stderr을 사용하기 때문에 부적절합니다.git
대표적인 예):모든 stderr 라인이 오류를 나타낸다고 가정할 수 없으며, stderr 출력이 있다고 해서 오류가 발생하는 것은 아닙니다.
영향을 받는 호스트:
오래된 Windows PowerShell ISE 및 Visual Studio Code 이외의 다른 오래된 GUI 기반 IDE.
PowerShell 원격을 통해 또는 백그라운드 작업에서 외부 프로그램을 실행할 때(이 두 호출 메커니즘은 동일한 인프라를 공유하며
ServerRemoteHost
PowerShell과 함께 제공되는 호스트).
원격이 아닌 백그라운드가 아닌 호출에서 예상대로 동작하는 호스트(stderr 라인을 디스플레이에 전달하고 정상적으로 인쇄합니다.
PowerShell 확장자가 포함된 Visual Studio 코드. 이 IDE(크로스 플랫폼 편집기)는 Windows PowerShell ISE를 대체하기 위한 것입니다.
이 GitHub 문제에서는 호스트 간의 이러한 불일치에 대해 설명합니다.
(참고: 대부분 추측입니다. PowerShell에서는 기본 명령을 거의 사용하지 않으며 PowerShell 내부에 대해 저보다 더 잘 알고 있는 사람도 있을 것입니다.)
PowerShell 콘솔 호스트에서 불일치를 발견한 것 같습니다.
- 하고 PowerShell을 .
NativeCommandError
. - PowerShell은 표준 오류 스트림을 모니터링하는 경우에만 이를 선택할 수 있습니다.
- PowerShell ISE는 콘솔 애플리케이션이 아니므로 기본 콘솔 애플리케이션에 쓸 콘솔이 없기 때문에 이를 모니터링해야 합니다.이것이 PowerShell ISE에서 이것이 다음과 관계없이 실패하는 이유입니다.
2>&1
리디렉션 연산자. - 를 사용하면 콘솔 호스트가 표준 오류 스트림을 모니터링합니다.
2>&1
표준 오류 스트림의 출력이 리디렉션되어 읽기 때문에 리디렉션 연산자입니다.
여기서는 콘솔 PowerShell 호스트가 게으르고 기본 콘솔이 출력을 처리할 필요가 없으면 콘솔 명령을 수동으로 실행할 수 있습니다.
PowerShell은 호스트 애플리케이션에 따라 다르게 동작하기 때문에 버그라고 생각합니다.
저에게는 ErrorAction Preference의 문제였습니다.ISE에서 실행할 때 첫 번째 줄에 $ErrorActionPreference = "Stop"을 설정했고 호출에 매개 변수로 *>&1이 추가된 모든 이벤트를 가로채는 것이었습니다.
그래서 처음에 저는 다음과 같은 대사를 했습니다.
& $exe $parameters *>&1
이전에 $ErrorActionPreference = "Stop" 파일이 있었기 때문에 작동하지 않았다고 말씀드렸듯이, (또는 스크립트를 시작하는 사용자를 위해 프로필에서 전체적으로 설정할 수 있습니다).
그래서 오류 액션을 강제로 적용하기 위해 Invoke-Expression으로 포장하려고 했습니다.
Invoke-Expression -Command "& `"$exe`" $parameters *>&1" -ErrorAction Continue
그리고 이것 또한 효과가 없습니다.
그래서 저는 일시적으로 오버라이드되는 ErrorAction Preference:
$old_error_action_preference = $ErrorActionPreference
try
{
$ErrorActionPreference = "Continue"
& $exe $parameters *>&1
}
finally
{
$ErrorActionPreference = $old_error_action_preference
}
그게 나한테 효과가 있어요.
그리고 저는 그것을 함수로 포장했습니다.
<#
.SYNOPSIS
Executes native executable in specified directory (if specified)
and optionally overriding global $ErrorActionPreference.
#>
function Start-NativeExecutable
{
[CmdletBinding(SupportsShouldProcess = $true)]
Param
(
[Parameter (Mandatory = $true, Position = 0, ValueFromPipelinebyPropertyName=$True)]
[ValidateNotNullOrEmpty()]
[string] $Path,
[Parameter (Mandatory = $false, Position = 1, ValueFromPipelinebyPropertyName=$True)]
[string] $Parameters,
[Parameter (Mandatory = $false, Position = 2, ValueFromPipelinebyPropertyName=$True)]
[string] $WorkingDirectory,
[Parameter (Mandatory = $false, Position = 3, ValueFromPipelinebyPropertyName=$True)]
[string] $GlobalErrorActionPreference,
[Parameter (Mandatory = $false, Position = 4, ValueFromPipelinebyPropertyName=$True)]
[switch] $RedirectAllOutput
)
if ($WorkingDirectory)
{
$old_work_dir = Resolve-Path .
cd $WorkingDirectory
}
if ($GlobalErrorActionPreference)
{
$old_error_action_preference = $ErrorActionPreference
$ErrorActionPreference = $GlobalErrorActionPreference
}
try
{
Write-Verbose "& $Path $Parameters"
if ($RedirectAllOutput)
{ & $Path $Parameters *>&1 }
else
{ & $Path $Parameters }
}
finally
{
if ($WorkingDirectory)
{ cd $old_work_dir }
if ($GlobalErrorActionPreference)
{ $ErrorActionPreference = $old_error_action_preference }
}
}
언급URL : https://stackoverflow.com/questions/10666101/lastexitcode-0-but-false-in-powershell-redirecting-stderr-to-stdout-gives
'programing' 카테고리의 다른 글
모양 전환 시작/종료에 대한 불균형 호출 (0) | 2023.08.16 |
---|---|
오류 jqxhr에 대한 Ajax GET URL (0) | 2023.08.16 |
PowerShell: 문자열과 일치하지 않는 경우 오류 종료 코드 반환 (0) | 2023.08.11 |
터미널을 통해 OS X의 adb에 액세스할 수 없습니다. "명령을 찾을 수 없습니다." (0) | 2023.08.11 |
자바스크립트에서 서버를 ping할 수 있습니까? (0) | 2023.08.11 |