about_Debuggers — PowerShell 디버거
about_Debuggers — PowerShell 디버거
PowerShell 디버거는 실행 중인 스크립트를 한 단계씩 살펴보면서 문제를 찾아내는 데 써요. 이 문서에서는 디버거를 시작하고 멈추는 방법, 디버거가 제공하는 명령, 중단점(breakpoint)으로 변수 값을 확인하는 법, 함수·원격 스크립트를 디버깅하는 법까지 차근차근 설명할게요.
출처: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_debuggers
본문
간단한 설명 (Short description)
PowerShell 디버거에 대해 설명해요.
자세한 설명 (Long description)
디버깅이란 스크립트가 실행되는 동안 들여다보면서, 스크립트 명령에 들어 있는 오류를 찾아 고치는 과정이에요. PowerShell 디버거를 쓰면 스크립트, 함수, 명령, PowerShell Desired State Configuration(DSC) 구성, 표현식에 들어 있는 오류와 비효율을 확인하고 찾아낼 수 있어요.
PowerShell 5.0부터 디버거가 개선되어서, 콘솔이나 Windows PowerShell ISE에서 실행되는 스크립트·함수·명령·구성·표현식뿐 아니라 원격 컴퓨터에서 실행되는 것들도 디버깅할 수 있어요.
참고
Windows PowerShell ISE는 Windows PowerShell만 지원해요. PowerShell 6 이상에서는 PowerShell용 확장이 설치된 Visual Studio Code를 사용해야 해요. 자세한 내용은 Debugging with Visual Studio Code 문서를 봐 주세요.
디버거 cmdlet (Debugger cmdlets)
PowerShell 디버거는 다음과 같은 cmdlet 세트를 포함하고 있어요.
Set-PSBreakpoint: 줄, 변수, 명령에 중단점을 설정해요.Get-PSBreakpoint: 현재 세션의 중단점을 가져와요.Disable-PSBreakpoint: 현재 세션의 중단점을 꺼요.Enable-PSBreakpoint: 현재 세션의 중단점을 다시 켜요.Remove-PSBreakpoint: 현재 세션에서 중단점을 삭제해요.Get-PSCallStack: 현재 호출 스택을 표시해요.
디버거 시작하기와 멈추기 (Starting and stopping the debugger)
디버거를 시작하려면 중단점을 하나 이상 설정한 다음, 디버깅할 스크립트·명령·함수를 실행하면 돼요.
중단점에 도달하면 실행이 멈추고, 제어권이 디버거로 넘어가요.
디버거를 멈추려면 스크립트·명령·함수를 끝까지 실행하거나, stop 또는 t를 입력하면 돼요.
디버거 명령 (Debugger commands)
PowerShell 콘솔에서 디버거를 사용할 때는 다음 명령으로 실행을 제어해요. Windows PowerShell ISE에서는 Debug 메뉴의 명령을 사용하면 돼요.
참고
다른 호스트 응용 프로그램에서 디버거를 사용하는 방법은 해당 호스트 응용 프로그램의 문서를 참고해 주세요.
s,StepInto: 다음 문장을 실행하고 멈춰요.v,StepOver: 다음 문장을 실행하되, 함수와 호출(invocation)은 건너뛰어요. 건너뛴 문장은 실행은 되지만, 단계별로 하나씩 들여다보지는 않아요.Ctrl+Break: (ISE에서는 Break All) PowerShell 콘솔이나 Windows PowerShell ISE에서 실행 중인 스크립트로 진입해요. 참고로 Windows PowerShell 2.0, 3.0, 4.0에서의 Ctrl+Break는 프로그램을 닫아 버린다는 점을 주의하세요. Break All은 로컬과 원격 양쪽에서 대화형으로 실행 중인 스크립트에 모두 적용돼요.o,StepOut: 현재 함수 밖으로 한 단계 나가요. 중첩된 상태라면 한 단계 위로 올라가요. 본문(main body)에 있다면 끝이나 다음 중단점까지 계속 실행해요. 건너뛴 문장은 실행은 되지만, 단계별로 들여다보지는 않아요.c,Continue: 스크립트가 끝나거나 다음 중단점에 도달할 때까지 계속 실행해요. 건너뛴 문장은 실행은 되지만, 단계별로 들여다보지는 않아요.l,List: 현재 실행 중인 스크립트의 부분을 표시해요. 기본적으로 현재 줄과 앞의 5줄, 뒤의 10줄을 보여줘요. 계속해서 목록을 보려면 ENTER를 누르면 돼요.l <m>,List:<m>으로 지정한 줄 번호부터 스크립트 16줄을 표시해요.l <m> <n>,List:<m>으로 지정한 줄 번호부터<n>줄을 표시해요.q,Stop,Exit: 스크립트 실행을 멈추고 디버거를 빠져나와요.Debug-Jobcmdlet으로 작업을 디버깅하고 있다면Exit명령은 디버거를 분리하고, 작업이 계속 실행되도록 놔둬요.k,Get-PSCallStack: 현재 호출 스택을 표시해요.<Enter>: 마지막 명령이Step(s),StepOver(v),List(l)였다면 그 명령을 반복해요. 그 외의 경우에는 제출(submit) 동작을 나타내요.?,h: 디버거 명령 도움말을 표시해요.
디버거를 빠져나가려면 Stop(q)을 사용하면 돼요.
PowerShell 5.0부터는 Debug-Job 또는 Debug-Runspace를 실행해서 시작한 중첩 디버깅 세션에서도 Exit 명령으로 빠져나올 수 있어요.
이런 디버거 명령을 활용하면 스크립트를 실행하다가 관심 있는 지점에서 멈춰서, 변수의 값과 시스템 상태를 살펴보고, 문제를 찾아낼 때까지 스크립트 실행을 계속 이어 갈 수 있어요.
참고
> 같은 리디렉션 연산자가 있는 문장으로 한 단계(step into) 들어가면, PowerShell 디버거는 스크립트에 남아 있는 모든 문장을 건너뛰어 버려요.
스크립트 변수의 값 표시하기 (Displaying the values of script variables)
디버거 안에서는 명령을 입력하거나, 변수 값을 표시하거나, cmdlet을 쓰거나, 명령줄에서 스크립트를 실행할 수도 있어요. 디버깅 중인 스크립트에 있는 모든 변수의 현재 값을 표시할 수 있는데, 다음 자동 변수(automatic variables)는 예외예요.
$_
$args
$input
$MyInvocation
$PSBoundParameters
이 변수들 중 어느 하나의 값을 표시하면, 디버거가 내부적으로 사용하는 파이프라인의 그 변수 값을 얻게 돼요. 스크립트에 있는 변수의 값이 아니에요.
디버깅 중인 스크립트의 이 변수 값들을 표시하려면, 스크립트에 이 값들을 새 변수로 저장하는 줄을 추가해 두고, 이 새 줄들 뒤에 중단점을 설정해 둬요. 그러면 새 변수의 값을 표시할 수 있어요.
예를 들어,
$scriptArgs = $args
$scriptname = $MyInvocation.PSCommandPath
디버거 환경 (The debugger environment)
중단점에 도달하면 디버거 환경으로 들어가요. 명령 프롬프트가 "[DBG]:"로 시작하도록 바뀌고, PowerShell 콘솔 같은 일부 호스트 응용 프로그램에서는 디버깅용 중첩 프롬프트가 열려요. 중첩 프롬프트는 프롬프트에 반복되는 큰-보다 큼 문자(ASCII 62)가 나타나는 것으로 알아볼 수 있어요.
프롬프트를 사용자 지정하는 방법은 about_Prompts 문서를 봐 주세요.
중첩 수준은 $NestedPromptLevel 자동 변수로 확인할 수 있어요. $PSDebugContext 자동 변수는 로컬 범위(scope)에 정의돼요. $PSDebugContext 변수의 존재 여부를 통해 디버거 안에서 실행 중인지 판단할 수 있어요.
예를 들어,
if ($PSDebugContext) {"Debugging"} else {"Not Debugging"}
디버깅할 때 $PSDebugContext 변수의 값을 사용할 수 있어요.
[DBG]: PS>>> $PSDebugContext.InvocationInfo
Name CommandLineParameters UnboundArguments Location
---- --------------------- ---------------- --------
= {} {} C:\ps-test\vote.ps1 (1)
디버깅과 범위 (Debugging and scope)
디버거로 진입한다고 해서 작업하고 있는 범위가 바뀌지는 않지만, 스크립트에서 중단점에 도달하면 스크립트 범위로 이동해요. 스크립트 범위는 디버거를 실행한 범위의 자식(child) 범위예요.
스크립트 범위에 정의된 변수와 별칭(alias)을 찾으려면 Get-Alias 또는 Get-Variable cmdlet의 Scope 매개 변수를 사용하면 돼요.
예를 들어, 다음 명령은 로컬(스크립트) 범위의 변수를 가져와요.
Get-Variable -Scope 0
이렇게 하면 스크립트에서 정의한 변수와 디버깅하는 동안 정의한 변수만 볼 수 있어서 유용해요.
명령줄에서 디버깅하기 (Debugging at the command line)
변수 중단점이나 명령 중단점을 설정할 때는 스크립트 파일에서만 중단점을 설정할 수 있어요. 하지만 기본적으로 그 중단점은 현재 세션에서 실행되는 모든 것에 적용돼요.
예를 들어 $name 변수에 중단점을 설정하면, 중단점을 끄거나 제거하기 전까지 실행하는 스크립트·명령·함수·스크립트 cmdlet·표현식에 있는 $name 변수마다 디버거가 멈춰요.
이렇게 하면 세션과 사용자 프로필에 있는 함수·변수·다른 스크립트의 영향을 받을 수 있는 더 실제적인 상황에서 스크립트를 디버깅할 수 있어요.
줄 중단점(line breakpoint)은 스크립트 파일에 특화되어 있어서, 스크립트 파일에만 설정돼요.
함수 디버깅하기 (Debugging functions)
begin, process, end 섹션이 있는 함수에 중단점을 설정하면, 디버거는 각 섹션의 첫 줄에서 멈춰요.
예를 들어,
function Test-Cmdlet {
begin {
Write-Output "Begin"
}
process {
Write-Output "Process"
}
end {
Write-Output "End"
}
}
C:\PS> Set-PSBreakpoint -Command Test-Cmdlet
C:\PS> Test-Cmdlet
Begin
Entering debug mode. Use h or ? for help.
Hit Command breakpoint on 'prompt:Test-Cmdlet'
Test-Cmdlet
[DBG]: C:\PS> c
Process
Entering debug mode. Use h or ? for help.
Hit Command breakpoint on 'prompt:Test-Cmdlet'
Test-Cmdlet
[DBG]: C:\PS> c
End
Entering debug mode. Use h or ? for help.
Hit Command breakpoint on 'prompt:Test-Cmdlet'
Test-Cmdlet
[DBG]: C:\PS>
원격 스크립트 디버깅하기 (Debugging remote scripts)
Enter-PSSession을 실행하면 대화형 원격 PowerShell 세션을 시작할 수 있어요. 이 세션에서 중단점을 설정하고 원격 컴퓨터의 스크립트 파일과 명령을 디버깅할 수 있어요. Enter-PSSession은 원격 컴퓨터에서 스크립트나 명령을 실행하는 중인 끊어진(disconnected) 세션에 다시 연결할 수도 있어요. 실행 중인 스크립트가 중단점에 걸리면 클라이언트 세션이 자동으로 디버거를 시작해요. 끊어진 세션에서 실행 중인 스크립트가 이미 중단점에 걸려 있었다면, 세션에 다시 연결할 때 Enter-PSSession이 자동으로 명령줄 디버거를 시작해요.
다음 예제는 이 과정이 어떻게 동작하는지 보여줘요. 스크립트의 6, 11, 22, 25줄에 중단점이 설정되어 있어요. 디버거가 시작되면 프롬프트에 두 가지 변화가 생겨요.
- 세션이 실행되고 있는 컴퓨터의 이름
- 디버깅 모드임을 알려 주는 DBG 프롬프트
Enter-PSSession -Cn localhost
[localhost]: PS C:\psscripts> Set-PSBreakpoint .\ttest19.ps1 6, 11, 22, 25
ID Script Line Command Variable Action
-- ------ ---- ------- -------- ------
0 ttest19.ps1 6
1 ttest19.ps1 11
2 ttest19.ps1 22
3 ttest19.ps1 25
[localhost]: PS C:\psscripts> .\ttest19.ps1
Hit Line breakpoint on 'C:\psscripts\ttest19.ps1:11'
At C:\psscripts\ttest19.ps1:11 char:1
+ $winRMName = "WinRM"
# + ~
[localhost]: [DBG]: PS C:\psscripts>> list
6: 1..5 | foreach { sleep 1; Write-Output "hello2day $_" }
7: }
# 8:
9: $count = 10
10: $psName = "PowerShell"
11:* $winRMName = "WinRM"
12: $myVar = 102
# 13:
14: for ($i=0; $i -lt $count; $i++)
15: {
16: sleep 1
17: Write-Output "Loop iteration is: $i"
18: Write-Output "MyVar is $myVar"
# 19:
20: hello2day
# 21:
[localhost]: [DBG]: PS C:\psscripts>> stepover
At C:\psscripts\ttest19.ps1:12 char:1
+ $myVar = 102
# + ~
[localhost]: [DBG]: PS C:\psscripts>> quit
[localhost]: PS C:\psscripts> Exit-PSSession
PS C:\psscripts>
예제 (Examples)
이 테스트 스크립트는 PowerShell 버전을 감지해서 버전에 맞는 메시지를 표시해요. 함수, 함수 호출, 변수를 포함하고 있어요.
다음 명령은 테스트 스크립트 파일의 내용을 표시해요.
PS C:\PS-test> Get-Content test.ps1
function psversion {
"PowerShell " + $PSVersionTable.PSVersion
if ($PSVersionTable.PSVersion.Major -lt 7) {
"Upgrade to PowerShell 7!"
}
else {
"Have you run a background job today (Start-Job)?"
}
}
$scriptName = $MyInvocation.PSCommandPath
psversion
"Done $scriptName."
시작하려면 스크립트에서 관심 있는 지점(줄, 명령, 변수, 함수)에 중단점을 설정해 보세요.
먼저 현재 디렉터리에 있는 Test.ps1 스크립트의 첫 줄에 줄 중단점을 만들어 볼게요.
PS C:\ps-test> Set-PSBreakpoint -Line 1 -Script test.ps1
이 명령은 System.Management.Automation.LineBreakpoint 개체를 반환해요.
Column : 0
Line : 1
Action :
Enabled : True
HitCount : 0
Id : 0
Script : C:\ps-test\test.ps1
ScriptName : C:\ps-test\test.ps1
이제 스크립트를 시작해 볼게요.
PS C:\ps-test> .\test.ps1
스크립트가 첫 중단점에 도달하면, 중단점 메시지가 디버거가 활성 상태임을 알려줘요. 이 메시지는 중단점을 설명하고, 스크립트의 첫 줄(함수 선언)을 미리 보여줘요. 또 프롬프트가 바뀌어 디버거가 제어권을 잡았음을 알려줘요.
미리보기 줄에는 스크립트 이름과 미리 볼 명령의 줄 번호가 포함돼요.
Entering debug mode. Use h or ? for help.
Hit Line breakpoint on 'C:\ps-test\test.ps1:1'
test.ps1:1 function psversion {
DBG>
Step 명령(s)을 사용해서 스크립트의 첫 문장을 실행하고 다음 문장을 미리 볼게요. 다음 문장은 $MyInvocation 자동 변수를 사용해서 $scriptName 변수에 스크립트 파일의 경로와 파일 이름을 설정해요.
DBG> s
test.ps1:11 $scriptName = $MyInvocation.PSCommandPath
이 시점에서는 $scriptName 변수가 아직 채워지지 않았어요. 하지만 변수 값을 표시해서 확인해 볼 수 있어요. 이 경우 값은 $null이에요.
DBG> $scriptname
DBG>
또 하나의 Step 명령(s)을 사용해서 현재 문장을 실행하고 스크립트의 다음 문장을 미리 볼게요. 다음 문장은 psversion 함수를 호출해요.
DBG> s
test.ps1:12 psversion
이 시점에서 $scriptName 변수는 채워졌지만, 변수 값을 확인해 볼게요. 이 경우 값은 스크립트 경로로 설정되어 있어요.
DBG> $scriptName
C:\ps-test\test.ps1
또 하나의 Step 명령을 사용해서 함수 호출을 실행해 볼게요. ENTER를 누르거나 Step을 뜻하는 "s"를 입력하면 돼요.
DBG> s
test.ps1:2 "PowerShell " + $PSVersionTable.PSVersion
디버그 메시지에는 함수 안의 문장 미리보기가 포함돼요. 이 문장을 실행하고 함수 안의 다음 문장을 미리 보려면 Step 명령을 쓸 수 있어요. 하지만 여기서는 StepOut 명령(o)을 사용해 볼게요. 이 명령은 (중단점에 걸리지 않는 한) 함수 실행을 끝내고 스크립트의 다음 문장으로 이동해요.
DBG> o
Windows PowerShell 2.0
Have you run a background job today (Start-Job)?
test.ps1:13 "Done $scriptName"
스크립트의 마지막 문장에 있으므로 Step, StepOut, Continue 명령은 모두 같은 효과를 내요. 여기서는 StepOut(o)을 사용할게요.
Done C:\ps-test\test.ps1
PS C:\ps-test>
StepOut 명령은 마지막 명령을 실행해요. 표준 명령 프롬프트는 디버거가 빠져나와 커맨드 프로세서에 제어권을 돌려줬음을 나타내요.
이제 디버거를 다시 실행해 볼게요. 먼저 현재 중단점을 삭제하려면 Get-PSBreakpoint와 Remove-PSBreakpoint cmdlet을 사용해요. (중단점을 다시 쓸 가능성이 있다면 Remove-PSBreakpoint 대신 Disable-PSBreakpoint cmdlet을 사용해도 돼요.)
PS C:\ps-test> Get-PSBreakpoint | Remove-PSBreakpoint
이 명령은 다음과 같이 줄여 쓸 수 있어요.
PS C:\ps-test> gbp | rbp
또는 다음 함수처럼 함수를 작성해서 명령을 실행할 수도 있어요.
function delbr { gbp | rbp }
이제 $scriptname 변수에 중단점을 만들어 볼게요.
PS C:\ps-test> Set-PSBreakpoint -Variable scriptname -Script test.ps1
이 명령은 다음과 같이 줄여 쓸 수 있어요.
PS C:\ps-test> sbp -V scriptname -S test.ps1
이제 스크립트를 시작해요. 스크립트는 변수 중단점에 도달해요. 기본 모드는 Write이므로, 변수 값을 바꾸는 문장 바로 앞에서 실행이 멈춰요.
PS C:\ps-test> .\test.ps1
Hit Variable breakpoint on 'C:\ps-test\test.ps1:$scriptName'
(Write access)
test.ps1:11 $scriptName = $MyInvocation.PSCommandPath
DBG>
$scriptName 변수의 현재 값($null)을 표시해 볼게요.
DBG> $scriptName
DBG>
Step 명령(s)을 사용해서 변수를 채우는 문장을 실행한 다음, $scriptName 변수의 새 값을 표시해 볼게요.
DBG> $scriptName
C:\ps-test\test.ps1
Step 명령(s)을 사용해서 스크립트의 다음 문장을 미리 볼게요.
DBG> s
test.ps1:12 psversion
다음 문장은 psversion 함수 호출이에요. 함수를 건너뛰되 실행은 하려면 StepOver 명령(v)을 사용하면 돼요. StepOver를 쓸 때 이미 함수 안에 있다면 효과가 없어요. 함수 호출은 표시되지만, 실행되지는 않아요.
DBG> v
Windows PowerShell 2.0
Have you run a background job today (Start-Job)?
test.ps1:13 "Done $scriptName"
StepOver 명령은 함수를 실행하고, 마지막 줄을 찍는 스크립트의 다음 문장을 미리 보여줘요.
Stop 명령(t)을 사용해서 디버거를 빠져나가 볼게요. 프롬프트가 표준 프롬프트로 돌아가요.
C:\ps-test>
중단점을 삭제하려면 Get-PSBreakpoint와 Remove-PSBreakpoint cmdlet을 사용해요.
PS C:\ps-test> Get-PSBreakpoint | Remove-PSBreakpoint
psversion 함수에 새 명령 중단점을 만들어 볼게요.
PS C:\ps-test> Set-PSBreakpoint -Command psversion -Script test.ps1
이 명령은 다음과 같이 줄여 쓸 수 있어요.
PS C:\ps-test> sbp -C psversion -S test.ps1
이제 스크립트를 실행해 볼게요.
PS C:\ps-test> .\test.ps1
Hit Command breakpoint on 'C:\ps-test\test.ps1:psversion'
test.ps1:12 psversion
DBG>
스크립트는 함수 호출에서 중단점에 도달해요. 이 시점에서는 함수가 아직 호출되지 않았어요. 이 때 Set-PSBreakpoint의 Action 매개 변수를 사용해서 중단점 실행 조건을 설정하거나, 로그를 시작하거나 진단·보안 스크립트를 호출하는 등 준비·진단 작업을 수행할 기회를 얻을 수 있어요.
액션을 설정하려면 Continue 명령(c)으로 스크립트를 빠져나오고, Remove-PSBreakpoint 명령으로 현재 중단점을 삭제해야 해요. (중단점은 읽기 전용이라서 현재 중단점에 액션을 추가할 수 없어요.)
DBG> c
Windows PowerShell 2.0
Have you run a background job today (Start-Job)?
Done C:\ps-test\test.ps1
PS C:\ps-test> Get-PSBreakpoint | Remove-PSBreakpoint
PS C:\ps-test>
이제 액션이 있는 새 명령 중단점을 만들어 볼게요. 다음 명령은 함수가 호출될 때 $scriptName 변수의 값을 로그로 기록하는 액션이 있는 명령 중단점을 설정해요. 액션에 break 키워드를 쓰지 않았으므로 실행은 멈추지 않아요. 백틱(`)은 줄-연속(line-continuation) 문자예요.
PS C:\ps-test> Set-PSBreakpoint -Command psversion -Script test.ps1 `
-Action { Add-Content "The value of `$scriptName is $scriptName." `
-Path action.log}
중단점의 조건을 설정하는 액션도 추가할 수 있어요. 다음 명령의 명령 중단점은 실행 정책이 RemoteSigned로 설정된 경우에만 실행돼요. RemoteSigned는 스크립트 실행을 여전히 허용하는 가장 제한적인 정책이에요.
PS C:\ps-test> Set-PSBreakpoint -Script test.ps1 -Command psversion `
-Action { if ((Get-ExecutionPolicy) -eq "RemoteSigned") { break }}
액션 안의 break 키워드는 디버거가 중단점을 실행하도록 지시해요. continue 키워드를 사용해서 멈추지 않고 실행하도록 지시할 수도 있어요. 기본 키워드는 continue이므로, 실행을 멈추게 하려면 break를 명시해야 해요.
이제 스크립트를 실행해 볼게요.
PS C:\ps-test> .\test.ps1
Hit Command breakpoint on 'C:\ps-test\test.ps1:psversion'
test.ps1:12 psversion
실행 정책이 RemoteSigned로 설정되어 있으므로 함수 호출에서 실행이 멈춰요.
이 시점에서 호출 스택을 확인해 보고 싶을 수 있어요. Get-PSCallStack cmdlet 또는 Get-PSCallStack 디버거 명령(k)을 사용하면 돼요. 다음 명령은 현재 호출 스택을 가져와요.
DBG> k
2: prompt
1: .\test.ps1: $args=[]
0: prompt: $args=[]
이 예제는 PowerShell 디버거를 사용하는 여러 방법 중 일부만 보여준 것뿐이에요.
PowerShell의 기타 디버깅 기능 (Other debugging features in PowerShell)
PowerShell 디버거 외에도 PowerShell에는 스크립트와 함수를 디버깅하는 데 쓸 수 있는 다른 기능들이 몇 가지 있어요.
Set-PSDebugcmdlet은 단계 실행(stepping)과 추적(tracing)을 포함한 아주 기본적인 스크립트 디버깅 기능을 제공해요.Set-StrictModecmdlet은 초기화되지 않은 변수에 대한 참조, 개체에 존재하지 않는 속성에 대한 참조, 유효하지 않은 함수 구문을 탐지하는 데 사용해요.- 스크립트에 진단 문장을 추가해요. 예를 들어 변수 값을 표시하는 문장, 명령줄에서 입력을 읽는 문장, 현재 명령을 보고하는 문장 같은 것들이에요. 이 작업에는
Write-Host,Write-Debug,Write-Warning,Write-Verbose처럼 Write 동사를 포함한 cmdlet을 사용해요.