about_Script_Blocks — PowerShell 스크립트 블록

about_Script_Blocks — PowerShell 스크립트 블록

PowerShell에서 한 줄로 마무리되지 않는 작업을 묶어 놓고 싶을 때가 있어요. 반복해서 쓸 명령 몇 개를 중괄호로 감싸 한 덩어리로 만들면, 그 덩어리를 마치 하나의 명령처럼 실행하고, 넘겨주고, 저장할 수 있어요. 그렇게 중괄호({})로 감싼 문장 모음이 바로 스크립트 블록(script block) 이에요.

출처: https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_script_blocks

본문

짧은 설명 (Short description)

스크립트 블록이 무엇인지 정의하고, PowerShell 프로그래밍 언어에서 스크립트 블록을 어떻게 사용하는지 설명해요.

긴 설명 (Long description)

PowerShell 프로그래밍 언어에서 스크립트 블록은 문장이나 식을 하나의 단위로 묶어 사용할 수 있게 해주는 모음이에요. 이 문장 모음은 중괄호({})로 감쌀 수도 있고, 함수로 정의할 수도 있으며, 스크립트 파일에 저장할 수도 있어요. 스크립트 블록은 값을 반환할 수 있고, 매개 변수(parameter)와 인수(argument)도 받을 수 있어요. 그리고 스크립트 블록은 변수에 대해 새로운 범위(scope) 를 만들어요.

문법적으로 스크립트 블록은 중괄호 안의 문장 목록(statement list)이에요. 아래 구문을 보면 돼요.

{<statement list>}

스크립트 블록은 그 안에 있는 모든 명령의 출력을 반환해요. 그 출력이 단일 객체일 수도 있고 배열일 수도 있어요.

반환 값을 명시적으로 지정하려면 return 키워드를 쓸 수도 있어요. return 키워드는 스크립트 블록이 반환하는 다른 출력에 영향을 주거나 막지 않아요. 다만 return 줄이 실행되면 그 시점에서 스크립트 블록이 종료돼요. 자세한 내용은 about_Return을 보면 돼요.

함수처럼 스크립트 블록도 매개 변수를 포함할 수 있어요. param 키워드를 사용해 이름 있는 매개 변수를 지정할 수 있는데, 구문은 아래와 같아요.

{
    param ([type]$Parameter1 [,[type]$Parameter2])
    <statement list>
}

함수처럼 스크립트 블록도 dynamicparam 문과 begin, process, end 문을 포함할 수 있어요. 자세한 내용은 about_Functionsabout_Functions_Advanced를 참고해요.

Important: 중괄호로 감쌌다고 전부 스크립트 블록은 아니에요. 예를 들어 for 반복문이나 if 문 안의 명령은 문장 블록(statement block) 으로 감싸여 있어요. 문장 블록은 변수에 대해 새 범위를 만들지 않고, 다른 문장 키워드의 매개 변수를 지원하지 않아요.

스크립트 블록 사용하기 (Using script blocks)

스크립트 블록은 Microsoft .NET Framework 형식 System.Management.Automation.ScriptBlock의 인스턴스예요. 명령은 스크립트 블록 매개 변수 값을 받을 수 있어요. 예를 들어 Invoke-Command cmdlet에는 스크립트 블록 값을 받는 ScriptBlock 매개 변수가 있어요.

Invoke-Command -ScriptBlock { Get-Process }
Handles  NPM(K)    PM(K)     WS(K) VM(M)   CPU(s)     Id ProcessName
-------  ------    -----     ----- -----   ------     -- -----------
999          28    39100     45020   262    15.88   1844 communicator
721          28    32696     36536   222    20.84   4028 explorer
...

Invoke-Command는 매개 변수 블록이 있는 스크립트 블록도 실행할 수 있어요. 매개 변수는 ArgumentList 매개 변수를 사용해 위치 기준으로 할당돼요.

Invoke-Command -ScriptBlock {
param($p1, $p2)

"p1: $p1"

"p2: $p2"

} -ArgumentList "First", "Second"
p1: First
p2: Second

위 예제의 스크립트 블록은 param 키워드를 사용해 $p1$p2 매개 변수를 만들어요. 문자열 "First"는 첫 번째 매개 변수($p1)에, "Second"는 $p2에 바인딩돼요. ArgumentList의 동작에 대한 자세한 내용은 about_Splatting을 보면 돼요.

변수를 사용해 스크립트 블록을 저장하고 실행할 수도 있어요. 아래 예제는 스크립트 블록을 변수에 저장한 뒤 Invoke-Command에 넘겨요.

$a = { Get-Service BITS }

Invoke-Command -ScriptBlock $a
Status   Name               DisplayName
------   ----               -----------
Running  BITS               Background Intelligent Transfer Ser...

호출 연산자(call operator) 도 변수에 저장된 스크립트 블록을 실행하는 또 다른 방법이에요. Invoke-Command처럼 호출 연산자도 자식 범위(child scope)에서 스크립트 블록을 실행해요. 호출 연산자는 스크립트 블록에 매개 변수를 사용하기 더 쉽게 만들어줘요.

$a ={ param($p1, $p2)

"p1: $p1"

"p2: $p2"

}
&$a -p2 "First" -p1 "Second"
p1: Second
p2: First

할당을 사용해 스크립트 블록의 출력을 변수에 저장할 수도 있어요.

PS>  $a = { 1 + 1}
PS>  $b = &$a
PS>  $b
2
PS>  $a = { 1 + 1}
PS>  $b = Invoke-Command $a
PS>  $b
2

호출 연산자에 대한 자세한 내용은 about_Operators를 참고해요.

매개 변수와 함께 지연 바인딩(delay-bind) 스크립트 블록 사용하기

파이프라인 입력을 받는 형식이 지정된 매개 변수는 매개 변수에 지연 바인딩(delay-bind) 스크립트 블록을 사용할 수 있게 해줘요. 지연 바인딩 스크립트 블록을 사용하면 파이프라인으로 들어오는 cmdlet에 대해 매개 변수를 실행 전에 정의하는 축약 표현으로 활용할 수 있어요.

지연 바인딩 스크립트 블록 안에서는 파이프라인 변수 $_를 사용해 파이프로 들어온 객체를 참조할 수 있어요.

# Both examples rename config.log to old_config.log

# Without delay-binding

dir config.log | ForEach-Object -Process {
  Rename-Item -Path $_ -NewName "old_$($_.Name)"
}

# With delay-binding

dir config.log | Rename-Item -NewName { "old_$($_.Name)" }

더 복잡한 cmdlet에서는 지연 바인딩 스크립트 블록을 사용해 파이프로 들어온 객체 하나를 재사용해 다른 매개 변수들도 채울 수 있어요.

매개 변수로 쓸 때 지연 바인딩 스크립트 블록에 대한 참고사항:

  • 지연 바인딩 스크립트 블록과 함께 쓰는 매개 변수 이름은 명시적으로 지정해야 해요.
  • 매개 변수는 형식이 없는(untyped) 상태여선 안 되고, 형식이 [scriptblock] 또는 [Object]여도 안 돼요.
  • 파이프라인 입력 없이 지연 바인딩 스크립트 블록을 사용하면 오류가 발생해요.
Rename-Item -NewName {$_.Name + ".old"}
Rename-Item : Cannot evaluate parameter 'NewName' because its argument is
specified as a scriptblock and there is no input. A scriptblock cannot
be evaluated without input.
At line:1 char:23
+  Rename-Item -NewName {$_.Name + ".old"}
+                       ~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : MetadataError: (:) [Rename-Item],
      ParameterBindingException
    + FullyQualifiedErrorId : ScriptBlockArgumentNoInput,
      Microsoft.PowerShell.Commands.RenameItemCommand

더 알아보기