about_Try_Catch_Finally
about_Try_Catch_Finally
try, catch, finally 블록으로 스크립트를 끝내버리는 오류를 잡고 대응하는 방법을 다뤄요. 오류가 나서 흐름이 끊기는 순간을 붙잡아서, 다시 스크립트를 정상 방향으로 되돌리는 게 핵심이에요. 자원을 정리해야 하는 경우까지 한 번에 볼게요.
본문
짧은 설명
try, catch, finally 블록을 사용해서 문 종료 오류(statement-terminating)와 스크립트 종료 오류(script-terminating)를 처리하는 방법을 설명해요.
자세한 설명
try, catch, finally 블록은 스크립트 안에서 오류에 응답하거나 처리하고 싶을 때 써요. PowerShell에는 종료 오류(terminating error)가 두 종류 있어요.
- 현재 문 하나를 멈추는 문 종료 오류
- 호출 스택 전체를 풀어버리는 스크립트 종료 오류
두 종류 모두 try/catch가 잡아요. 종료 오류를 처리하는 방법으로 trap 문을 쓸 수도 있고요, 자세한 내용은 about_Trap을 참고해요. 모든 오류 종류에 대한 종합적인 설명은 about_Error_Handling에서 볼 수 있어요.
문 종료 오류는 현재 문 실행을 멈추지만, 그 오류가 동시에 스크립트 종료 오류가 아니라면 PowerShell은 다음 문으로 계속 진행해요. 반면 스크립트 종료 오류(예를 들어 throw 키워드가 만든 오류)는 try/catch 블록이나 trap 문이 잡지 않으면 스크립트 전체를 멈춰요.
참고 —
$ErrorActionPreference는throw를 억제할 수 있어요. 이 값이SilentlyContinue나Ignore로 설정되면 오류가 전파되지 않고, 실행은 다음 문에서 계속돼요. 자세한 내용은 about_Error_Handling의 스크립트 종료 오류 섹션을 참고해요.
try 블록은 스크립트에서 오류를 감시하고 싶은 구간을 정해요. try 블록 안에서 오류가 나면, 그 오류는 먼저 $Error 자동 변수에 저장돼요. 그다음 PowerShell은 오류를 처리할 catch 블록을 찾아요. try 문에 맞는 catch 블록이 없으면, 부모 스코프에서 알맞은 catch 블록이나 trap 문을 계속 찾아요. catch 블록이 끝나거나, 알맞은 catch 블록이나 trap 문을 찾지 못하면 finally 블록이 실행돼요. 만약 오류를 처리할 수 없다면, 그 오류는 오류 스트림으로 기록돼요.
catch 블록에는 오류를 추적하거나 스크립트의 예상 흐름을 복구하는 명령을 담을 수 있어요. catch 블록은 잡을 오류 종류를 지정할 수도 있고, try 문은 서로 다른 오류 종류를 위한 catch 블록을 여러 개 가질 수 있어요.
finally 블록은 스크립트가 더 이상 필요로 하지 않는 자원을 해제하는 데 쓸 수 있어요.
try, catch, finally는 C# 프로그래밍 언어에서 쓰이는 try, catch, finally 키워드와 닮았어요.
구문
try 문은 try 블록 하나, catch 블록 0개 이상, finally 블록 0개 또는 1개로 이루어져요. try 문은 catch 블록이나 finally 블록을 반드시 하나 이상 가져야 해요.
다음은 try 블록 구문이에요.
try {<statement list>}
try 키워드 뒤에는 중괄호로 감싼 명령문 목록이 따라와요. 명령문 목록을 실행하는 동안 종료 오류가 발생하면, 스크립트는 try 블록의 오류 객체를 알맞은 catch 블록으로 넘겨요.
다음은 catch 블록 구문이에요.
catch [[<error type>][',' <error type>]*] {<statement list>}
오류 종류는 대괄호 안에 나타나요. 가장 바깥쪽 대괄호는 그 요소가 선택 사항이라는 뜻이에요.
catch 키워드 뒤에는 선택적인 오류 종류 지정 목록과 명령문 목록이 따라와요. try 블록에서 종료 오류가 발생하면 PowerShell은 알맞은 catch 블록을 찾아요. 찾으면 그 catch 블록 안의 명령문들이 실행돼요.
catch 블록은 오류 종류를 하나 이상 지정할 수 있어요. 오류 종류는 Microsoft .NET Framework 예외이거나, .NET Framework 예외에서 파생된 예외예요. catch 블록은 지정된 .NET Framework 예외 클래스의 오류나, 그 클래스에서 파생된 클래스의 오류를 처리해요.
catch 블록이 오류 종류를 지정하면 그 catch 블록은 해당 종류의 오류만 처리해요. 오류 종류를 지정하지 않으면 그 catch 블록은 try 블록에서 발생한 어떤 오류든 처리해요. try 문은 지정한 여러 오류 종류에 따라 catch 블록을 여러 개 둘 수 있어요.
다음은 finally 블록 구문이에요.
finally {<statement list>}
finally 키워드 뒤에는 명령문 목록이 따라와요. 이 목록은 스크립트가 실행될 때마다 항상 실행돼요. try 문이 오류 없이 실행됐든, catch 문에서 오류를 잡았든 관계없이요.
참고로 CTRL+C를 누르면 파이프라인이 멈춰요. 파이프라인으로 보낸 객체는 출력으로 표시되지 않아요. 그래서 "Finally block has run" 같은 표시용 명령문을 넣어도, finally 블록이 실제로 실행됐더라도 CTRL+C를 누르고 난 뒤에는 화면에 표시되지 않아요.
오류 잡기
다음 예시 스크립트는 catch 블록이 딸린 try 블록을 보여줘요.
try { NonsenseString }
catch { "An error occurred." }
catch 키워드는 반드시 try 블록이나 다른 catch 블록 바로 뒤에 와야 해요.
PowerShell은 "NonsenseString"을 cmdlet이나 다른 항목으로 인식하지 못해요. 이 스크립트를 실행하면 다음 결과가 나와요.
An error occurred.
스크립트가 "NonsenseString"을 만나면 종료 오류를 일으켜요. catch 블록은 블록 안의 명령문 목록을 실행해서 그 오류를 처리해요.
여러 개의 catch 문 사용하기
try 문은 catch 블록을 몇 개든 가질 수 있어요. 예를 들어 다음 스크립트는 try 블록이 MyDoc.doc를 내려받고, catch 블록을 두 개 가져요.
try {
$wc = New-Object System.Net.WebClient
$wc.DownloadFile("https://httpbin.org/MyDoc.doc","C:\temp\MyDoc.doc")
} catch [System.Net.WebException],[System.IO.IOException] {
"Unable to download MyDoc.doc from https://httpbin.org."
} catch {
"An error occurred that could not be resolved."
}
첫 번째 catch 블록은 System.Net.WebException과 System.IO.IOException 종류의 오류를 처리해요. 두 번째 catch 블록은 오류 종류를 지정하지 않았으니, 그 외에 발생하는 다른 모든 종료 오류를 처리해요.
PowerShell은 오류 종류를 상속 관계로 매칭해요. catch 블록은 지정한 .NET Framework 예외 클래스나, 그 클래스에서 파생된 클래스의 오류를 처리해요. 다음 예시는 "Command Not Found" 오류를 잡는 catch 블록이에요.
catch [System.Management.Automation.CommandNotFoundException] {
"Inherited Exception"
}
지정한 오류 종류 CommandNotFoundException은 System.SystemException 종류에서 상속받아요. 다음 예시도 Command Not Found 오류를 잡아요.
catch [System.SystemException] {"Base Exception" }
이 catch 블록은 "Command Not Found" 오류와, SystemException 종류에서 상속받는 다른 오류를 처리해요.
오류 클래스와 그 파생 클래스를 함께 지정한다면, 파생 클래스용 catch 블록을 일반 클래스용 catch 블록보다 앞에 두어야 해요.
참고 — PowerShell은 모든 예외를 RuntimeException 종류로 감싸요. 그래서 System.Management.Automation.RuntimeException 오류 종류를 지정하는 것은, 오류 종류를 지정하지 않은
catch블록과 똑같이 동작해요.
try catch 안에서 trap 사용하기
try 블록 안에 trap이 정의되어 있고 그 try 블록에서 종료 오류가 발생하면, 맞는 catch 블록이 있더라도 trap 문이 제어권을 가져요.
trap이 try보다 상위 블록에 있고, 현재 스코프 안에 맞는 catch 블록이 없다면, 부모 스코프에 맞는 catch 블록이 있더라도 trap이 제어권을 가져요.
예외 정보 접근하기
catch 블록 안에서는 $_ 또는 $PSItem 자동 변수로 현재 오류를 접근할 수 있어요. 이 객체의 형식은 ErrorRecord예요.
try { NonsenseString }
catch {
Write-Host "An error occurred:"
Write-Host $_
}
이 스크립트를 실행하면 다음 결과가 나와요.
An Error occurred:
The term 'NonsenseString' is not recognized as the name of a cmdlet, function,
script file, or operable program. Check the spelling of the name, or if a path
was included, verify that the path is correct and try again.
ScriptStackTrace, Exception, ErrorDetails처럼 접근할 수 있는 추가 속성도 있어요. 예를 들어 스크립트를 이렇게 바꿔볼게요.
try { NonsenseString }
catch {
Write-Host "An error occurred:"
Write-Host $_.ScriptStackTrace
}
결과는 대략 이렇게 나와요.
An Error occurred:
at <ScriptBlock>, <No file>: line 2
finally로 리소스 해제하기
스크립트가 사용한 자원을 해제하려면 try와 catch 블록 뒤에 finally 블록을 추가해요. finally 블록의 문장은 try 블록이 종료 오류를 만났는지와 관계없이 항상 실행돼요. PowerShell은 스크립트가 끝나기 전이나, 현재 블록이 스코프를 벗어나기 전에 finally 블록을 실행해요.
finally 블록은 CTRL+C로 스크립트를 멈춰도 실행돼요. catch 블록 안에서 exit 키워드가 스크립트를 멈추게 해도 역시 실행되요.
다음 예시에서 try 블록은 파일을 C:\temp 폴더로 내려받으려고 해요. catch 블록은 내려받는 동안 발생하는 오류를 처리하고, finally 블록은 WebClient 객체를 정리하고 임시 파일이 있으면 제거해요.
try {
$wc = New-Object System.Net.WebClient
$tempFile = "C:\temp\MyDoc.doc"
$wc.DownloadFile("https://httpbin.org/MyDoc.doc",$tempFile)
} catch [System.Net.WebException],[System.IO.IOException] {
"Unable to download MyDoc.doc from https://httpbin.org."
} catch {
"An error occurred that could not be resolved."
} finally {
$wc.Dispose()
if (Test-Path $tempFile) { Remove-Item $tempFile }
}