HOWTO · PowerShell

Windows PowerShell에서 스크립트를 종료하는 방법

PowerShell에서 exit, return, throw, break, continue, Stop-Process를 구분하여 올바른 범위를 끝내고 신뢰할 수 있는 종료 코드를 반환하는 방법을 알아봅니다.

이 페이지의 내용

PowerShell 스크립트를 종료한다는 말은 한 가지 동작만 뜻하지 않습니다. 스크립트 프로세스 전체, 현재 함수, 루프 반복, 별도의 운영 체제 프로세스 중 무엇을 멈출지에 따라 올바른 문이 달라집니다.

목적 사용할 문
최상위 스크립트를 끝내고 상태 반환 exit <코드>
함수, 스크립트 또는 스크립트 블록에서 나가기 return
호출자가 잡을 수 있는 오류 알리기 throw
루프 또는 switch에서 나가기 break
다음 루프 또는 switch 항목으로 이동 continue
별도의 로컬 프로세스 종료 Stop-Process

exit로 PowerShell 스크립트 종료하기

exit는 스크립트 또는 PowerShell 인스턴스를 종료합니다. 선택적으로 지정한 정수는 프로세스 종료 코드가 됩니다. 일반적으로 0은 성공, 0이 아닌 값은 실패를 뜻합니다. 예약 작업, CI 작업, 래퍼 프로그램이 올바르게 대응할 수 있도록 각 오류 코드의 의미를 문서화하십시오.

다음 진입 스크립트를 Check-Config.ps1로 저장합니다.

param([Parameter(Mandatory)][string]$Path)

if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) {
    Write-Error "Configuration file not found: $Path"
    exit 2
}

Write-Output 'Configuration file found.'
exit 0

다른 PowerShell 세션에서 자식 프로세스로 실행한 뒤 상태를 확인합니다.

powershell.exe -NoProfile -File .\Check-Config.ps1 -Path .\missing.json
$LASTEXITCODE

마지막 명령은 2를 출력합니다. PowerShell 7에서는 powershell.exe 대신 pwsh를 사용합니다. PowerShell 호출자는 $LASTEXITCODE에서 자식 프로세스 상태를 읽고, cmd.exe 호출자는 %ERRORLEVEL%에서 읽습니다.

exit는 최상위 스크립트 경계에서만 사용하는 편이 안전합니다. 재사용 함수가 exit를 호출하면 대화형 세션, 테스트 실행기 또는 해당 함수를 호출한 호스트까지 닫을 수 있습니다. 함수는 데이터를 반환하거나 오류를 발생시키고, 진입 스크립트만 결과를 종료 코드로 변환하도록 구성하십시오.

return으로 현재 범위 나가기

return은 현재 함수, 스크립트 또는 스크립트 블록에서 나갑니다. 이 문 자체가 프로세스 종료 코드를 설정하지는 않습니다.

function Get-ConfigText {
    param([string]$Path)

    if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) {
        return $null
    }

    Get-Content -LiteralPath $Path -Raw
}

일반 PowerShell 함수에서는 return 뒤의 식뿐 아니라 캡처되지 않은 성공 스트림 값이 모두 출력에 포함됩니다. 진단 메시지는 추가 문자열을 Write-Output으로 보내지 말고 Write-Verbose 같은 적절한 스트림에 기록하십시오.

throw로 잡을 수 있는 오류 발생시키기

throw는 기본적으로 스크립트를 종료하는 오류를 만들고 catch 블록 또는 trap이 처리할 때까지 호출 스택을 되감습니다. 함수가 유효한 결과를 만들 수 없고 호출자가 복구 방법을 결정해야 할 때 사용합니다.

function Get-RequiredConfig {
    param([string]$Path)

    if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) {
        throw "Configuration file not found: $Path"
    }

    Get-Content -LiteralPath $Path -Raw
}

try {
    $config = Get-RequiredConfig -Path '.\settings.json'
}
catch {
    Write-Error $_
    exit 2
}

이 패턴은 책임을 분리합니다. 함수는 throw로 실패 이유를 전달하고, 진입 스크립트는 이를 잡아 외부에 공개할 종료 코드를 선택합니다. 모든 cmdlet 오류가 자동으로 catch에 도달하는 것은 아닙니다. 비종료 오류를 여기서 처리해야 한다면 -ErrorAction Stop을 지정하십시오.

breakcontinue는 제어 블록에서만 사용하기

break는 가장 가까운 루프 또는 switch를 끝냅니다. continue는 현재 반복의 나머지를 건너뛰고 다음 반복을 시작합니다.

foreach ($name in 'alpha', '', 'beta', 'stop', 'omega') {
    if ([string]::IsNullOrWhiteSpace($name)) {
        continue
    }
    if ($name -eq 'stop') {
        break
    }
    Write-Output $name
}

출력:

alpha
beta

두 키워드는 일반적인 스크립트 종료 명령이 아닙니다. 루프, switch, trap 밖에서 사용하면 PowerShell이 호출 스택에서 바깥쪽 제어 구조를 찾으며, 찾지 못한 경우 현재 런스페이스를 종료할 수 있습니다.

Stop-Process로 다른 프로세스 종료하기

Stop-Process는 로컬 컴퓨터의 별도 프로세스를 대상으로 하며 현재 스크립트를 종료한다는 뜻이 아닙니다. 넓은 조건을 사용할 때는 먼저 -WhatIf로 대상을 미리 확인하십시오.

Get-Process -Name notepad -ErrorAction SilentlyContinue |
    Stop-Process -WhatIf

대상을 확인한 뒤에만 -WhatIf를 제거하십시오. 하나의 이름이 여러 프로세스와 일치할 수 있다면 알려진 프로세스 개체나 PID를 사용하는 것이 좋습니다. 다른 사용자가 소유한 프로세스를 종료하려면 관리자 권한 PowerShell 세션이 필요할 수도 있습니다.

네이티브 프로그램의 종료 코드 보존하기

$?는 마지막 PowerShell 명령의 성공 여부를 나타내고, $LASTEXITCODE는 마지막 네이티브 프로그램의 종료 코드를 저장합니다. 다음 네이티브 명령이 값을 덮어쓸 수 있으므로 즉시 보관하십시오.

git status --porcelain
$gitCode = $LASTEXITCODE

if ($gitCode -ne 0) {
    Write-Error "git failed with exit code $gitCode"
    exit $gitCode
}

재사용 가능한 로직에서는 return 또는 throw를 사용하고 exit <코드>는 최상위 스크립트 경계에만 두십시오. 이렇게 하면 함수 출력, 제어 흐름, 운영 체제 프로세스 상태를 명확히 구분할 수 있습니다.

정리를 보장하고 취소와 구분하기

처리를 끝낼 수 있는 줄 뒤가 아니라 finally에 정리 작업을 둡니다. PowerShell 언어 키워드 문서에 따르면 finallytry 작업이 성공할 때, 오류가 catch에 도달할 때, exit가 호출될 때, 또는 Ctrl+C가 스크립트를 중단할 때에도 실행됩니다. 따라서 스트림, 잠금 또는 임시 파일을 해제하기에 적합합니다.

$stream = $null

try {
    $stream = [System.IO.File]::OpenRead($Path)
    # Process the stream.
}
finally {
    if ($null -ne $stream) {
        $stream.Dispose()
    }
}

재사용 코드가 작업 중간에 exit를 호출하지 말고 진입 스크립트까지 throw 또는 return하도록 하십시오. 그러면 정리, 진단, 프로세스 상태를 한 곳에서 선택할 수 있습니다. Ctrl+C, 중지된 작업, 잘못된 입력은 서로 다른 이벤트입니다. 각각을 취소, 유효성 검사 실패 또는 호출자·스케줄러가 처리해야 할 다른 상태 중 무엇으로 볼지 정하고 문서화하십시오. 이를 조용히 일반적인 실패 하나로 합치지 마십시오. 다른 프로세스를 끝내는 일은 또 다르므로 가능하면 정상 종료를 우선하십시오. Stop-Process -Force가 파일이나 상태 정리를 막을 수 있음을 이해할 때만 사용하십시오.

자식 스크립트와 호스트를 의도한 범위에 유지하기

호출 연산자는 자식 스크립트를 자체 스크립트 범위에서 실행합니다. 도트 소싱은 현재 범위에서 실행하고 함수와 변수를 가져옵니다. 정의를 가져오는 것이 목적일 때만 사용하십시오.

# Run the child in its own script scope.
& .\Child.ps1

# Import definitions into the current scope.
. .\Functions.ps1

재사용 코드의 exitreturn보다 강하며 대화형 세션, 테스트 실행기 또는 다른 호스트를 닫을 수 있습니다. powershell.exe -File 또는 pwsh -File로 시작한 자식 프로세스는 호출자에게 제어를 돌려주지만, 대화형 콘솔은 현재 세션을 닫을 수 있습니다. 포함된 런스페이스와 편집기는 다를 수 있습니다. -File-Command, 프로필, 인용, 출력, 종료 상태를 포함해 실제 운영 명령줄을 각각 확인하십시오.

권장 사항

의도한 최상위 경계에서만 exit <코드>를, 현재 범위에는 return을, 호출자가 처리할 실패에는 throw를 사용하십시오. breakcontinue는 해당 루프나 switch가 보이는 곳에서만 사용하고 Stop-Process는 별도의 로컬 프로세스에만 사용하십시오. 데이터를 반환하거나 오류를 던지는 함수는 재사용하기 쉬우며, 작은 진입 스크립트가 결과를 셸, 스케줄러, CI 또는 래퍼가 사용할 안정된 상태로 변환할 수 있습니다.