about_Parameter_Sets — 파라미터 집합(Parameter Set) 다루기

about_Parameter_Sets — 파라미터 집합(Parameter Set) 다루기

파라미터 집합(parameter set)은 파워셸의 고급 함수(advanced function)를 쓸 때 가장 유용한 기능 중 하나예요. 하나의 함수를 여러 상황에서 다르게 동작하도록 만들고 싶을 때가 있죠. 예를 들어 어떤 입력을 받으면 파일 개수를 세고, 다른 입력을 받으면 내용의 줄 수를 셀 수 있도록요. 이렇게 하나의 함수가 시나리오에 따라 다른 동작을 하게 해 주는 장치가 파라미터 집합이에요. 사용자에게 노출되는 파라미터를 시나리오별로 다르게 보여 줄 수도 있고, 지정한 파라미터에 따라 돌려주는 결과도 달라질 수 있어요.

여기서 중요한 점이 하나 있어요. 한 번에 하나의 파라미터 집합만 사용할 수 있어요. 여러 집합을 동시에 쓰는 건 파워셸이 허용하지 않아요.

출처: about_Parameter_Sets — Microsoft Learn

본문

파라미터 집합의 요구사항

모든 파라미터 집합에 공통으로 적용되는 규칙이 몇 가지 있어요. 먼저 이 규칙들을 짚고 넘어갈게요.

  • 파라미터에 집합 이름을 지정하지 않으면, 그 파라미터는 모든 파라미터 집합에 속해요.
  • 각 파라미터 집합은 고유한 파라미터 조합을 가져야 해요. 가능하면 그 고유 파라미터 중 적어도 하나는 필수(mandatory) 파라미터로 두는 게 좋아요.
  • 여러 위치 파라미터(positional parameter)를 가진 집합은 각 파라미터에 서로 다른 위치를 지정해야 해요. 같은 위치를 두 파라미터가 가질 수 없어요.

참고로 파라미터 집합은 최대 32개까지 만들 수 있어요.

기본 파라미터 집합 (Default parameter set)

파라미터 집합이 여러 개 정의되면, CmdletBinding 특성의 DefaultParameterSetName 키워드가 기본 집합을 지정해요. 파워셸은 명령에 전달된 정보만으로 어떤 집합을 써야 할지 판단할 수 없을 때 이 기본 파라미터 집합을 사용해요. CmdletBinding 특성에 대해 더 알고 싶으면 about_Functions_CmdletBindingAttribute 문서를 확인해 보세요.

파라미터 집합 선언하기

파라미터 집합을 만들려면 집합 안의 모든 파라미터에 Parameter 특성의 ParameterSetName 키워드를 지정해야 해요. 여러 집합에 속하는 파라미터라면, 그 집합 수만큼 Parameter 특성을 붙여 주면 돼요.

Parameter 특성 덕분에 파라미터를 집합마다 다르게 정의할 수 있어요. 예를 들어 어떤 집합에서는 필수로, 다른 집합에서는 선택 사항으로 만들 수 있죠. 다만 각 파라미터 집합은 반드시 고유 파라미터를 하나 이상 가져야 해요.

집합 이름을 지정하지 않은 파라미터는 앞서 말한 대로 모든 집합에 속하게 돼요.

예약된 집합 이름: __AllParameterSets

파워셸은 특별한 처리를 위해 __AllParameterSets라는 이름을 예약해 두고 있어요.

  • 명시적인 기본 이름을 쓰지 않으면 __AllParameterSets가 기본 파라미터 집합의 이름이 돼요.
  • Parameter 특성의 ParameterSetName__AllParameterSets로 지정하는 건, 애초에 ParameterSetName을 지정하지 않는 것과 같아요. 두 경우 모두 파라미터가 모든 집합에 속하게 되죠.

한 가지 주의할 점이 있어요. CmdletBinding 특성은 DefaultParameterSetName__AllParameterSets로 설정하는 걸 막아 주지 않아요. 만약 그렇게 하면, 파워셸이 Parameter 특성으로 제대로 참조할 수 없는 명시적 파라미터 집합을 만들어 버리게 되니 조심하세요.

예시: 파일 줄·문자·단어 수 세기

다음 예시 함수는 텍스트 파일의 줄 수, 문자 수, 단어 수를 세는 함수예요. 파라미터를 이용해서 어떤 값을 돌려받을지, 어떤 파일을 대상으로 할지를 지정할 수 있어요. 여기에는 네 개의 파라미터 집합이 정의돼 있어요.

  • Path
  • PathAll
  • LiteralPath
  • LiteralPathAll
function Measure-Lines {
 [CmdletBinding(DefaultParameterSetName = 'Path')]
 param (
 [Parameter(Mandatory, ParameterSetName = 'Path', Position = 0)]
 [Parameter(Mandatory, ParameterSetName = 'PathAll', Position = 0)]
 [string[]]$Path,

 [Parameter(Mandatory, ParameterSetName = 'LiteralPathAll', ValueFromPipeline)]
 [Parameter(Mandatory, ParameterSetName = 'LiteralPath', ValueFromPipeline)]
 [string[]]$LiteralPath,

 [Parameter(ParameterSetName = 'Path')]
 [Parameter(ParameterSetName = 'LiteralPath')]
 [switch]$Lines,

 [Parameter(ParameterSetName = 'Path')]
 [Parameter(ParameterSetName = 'LiteralPath')]
 [switch]$Words,

 [Parameter(ParameterSetName = 'Path')]
 [Parameter(ParameterSetName = 'LiteralPath')]
 [switch]$Characters,

 [Parameter(Mandatory, ParameterSetName = 'PathAll')]
 [Parameter(Mandatory, ParameterSetName = 'LiteralPathAll')]
 [switch]$All,

 [Parameter(ParameterSetName = 'Path')]
 [Parameter(ParameterSetName = 'PathAll')]
 [switch]$Recurse
 )

 begin {
 if ($All) {
 $Lines = $Words = $Characters = $true
 }
 elseif (($Words -eq $false) -and ($Characters -eq $false)) {
 $Lines = $true
 }
 }
 process {
 if ($Path) {
 $Files = Get-ChildItem -Path $Path -Recurse:$Recurse -File
 }
 else {
 $Files = Get-ChildItem -LiteralPath $LiteralPath -File
 }
 foreach ($file in $Files) {
 $result = [ordered]@{ }
 $result.Add('File', $file.FullName)

 $content = Get-Content -LiteralPath $file.FullName

 if ($Lines) { $result.Add('Lines', $content.Length) }

 if ($Words) {
 $wc = 0
 foreach ($line in $content) { $wc += $line.Split(' ').Length }
 $result.Add('Words', $wc)
 }

 if ($Characters) {
 $cc = 0
 foreach ($line in $content) { $cc += $line.Length }
 $result.Add('Characters', $cc)
 }

 New-Object -TypeName psobject -Property $result
 }
 }
}

각 파라미터 집합은 고유한 파라미터 하나 또는 고유한 조합을 가져야 해요. PathPathAll 집합은 아주 비슷하지만, All 파라미터가 PathAll 집합에만 있어요. LiteralPathLiteralPathAll도 마찬가지예요. PathAllLiteralPathAll 둘 다 All 파라미터를 갖고 있지만, PathLiteralPath 파라미터가 이 둘을 구분해 주는 거예요.

Get-Command -Syntax로 각 파라미터 집합의 구문은 볼 수 있지만, 집합의 이름은 안 보여요. 다음 예시처럼 하면 각 집합에서 어떤 파라미터를 쓸 수 있는지 확인할 수 있어요.

(Get-Command Measure-Lines).ParameterSets |
 Select-Object -Property @{n='ParameterSetName';e={$_.Name}},
 @{n='Parameters';e={$_.ToString()}}
ParameterSetName Parameters
---------------- ----------
Path [-Path] <string[]> [-Lines] [-Words] [-Characters] [-Recurse] [<CommonParameters>]
PathAll [-Path] <string[]> -All [-Recurse] [<CommonParameters>]
LiteralPath -LiteralPath <string[]> [-Lines] [-Words] [-Characters] [<CommonParameters>]
LiteralPathAll -LiteralPath <string[]> -All [<CommonParameters>]

파라미터 집합이 실제로 동작하는 모습

다음 예시는 PathAll 파라미터 집합을 사용한 모습이에요.

Measure-Lines test* -All
File Lines Words Characters
---- ----- ----- ----------
C:\temp\test\test.help.txt 31 562 2059
C:\temp\test\test.md 30 1527 3224
C:\temp\test\test.ps1 3 3 79
C:\temp\test\test[1].txt 31 562 2059

여러 집합의 파라미터를 섞어 쓰면 생기는 오류

이번에는 서로 다른 파라미터 집합의 고유 파라미터를 함께 써 보는 예시예요.

Get-ChildItem -Path $PSHOME -LiteralPath $PSHOME
Get-ChildItem: Parameter set cannot be resolved using the specified named
parameters. One or more parameters issued cannot be used together or an
insufficient number of parameters were provided.

PathLiteralPathGet-ChildItem 커맨드릿의 서로 다른 파라미터 집합에 속하는 고유 파라미터예요. 이 둘을 같은 커맨드릿 호출에 함께 쓰면 오류가 나요. 커맨드릿 호출 한 번에는 파라미터 집합 하나만 쓸 수 있기 때문이에요.

어떤 파라미터 집합이 사용됐는지 알아내기

자동 변수 $PSCmdletParameterSetName 속성이 있어요. 이 속성에는 현재 사용 중인 파라미터 집합의 이름이 들어 있어요. 함수 안에서 이 속성을 이용하면 집합에 따라 다른 동작을 선택할 수 있어요.

function Get-ParameterSetName {

 [CmdletBinding(DefaultParameterSetName = 'Set1')]
 param (
 [Parameter(ParameterSetName = 'Set1', Position = 0)]
 $Var1,

 [Parameter(ParameterSetName = 'Set2', Position = 0)]
 $Var2,

 [Parameter(ParameterSetName = 'Set1', Position = 1)]
 [Parameter(ParameterSetName = 'Set2', Position = 1)]
 $Var3,

 [Parameter(Position = 2)]
 $Var4
 )

 "Using Parameter set named '$($PSCmdlet.ParameterSetName)'"

 switch ($PSCmdlet.ParameterSetName) {
 'Set1' {
 "`$Var1 = $Var1"
 "`$Var3 = $Var3"
 "`$Var4 = $Var4"
 break
 }
 'Set2' {
 "`$Var2 = $Var2"
 "`$Var3 = $Var3"
 "`$Var4 = $Var4"
 break
 }
 }
}
PS> Get-ParameterSetName 1 2 3
Using Parameter set named 'Set1'
$Var1 = 1
$Var3 = 2
$Var4 = 3
PS> Get-ParameterSetName -Var2 1 2 3
Using Parameter set named 'Set2'
$Var2 = 1
$Var3 = 2
$Var4 = 3

Get-ParameterSetName 1 2 3처럼 위치 파라미터만으로 호출하면 Set1이, -Var2를 명시하면 Set2가 선택되는 걸 볼 수 있어요. 이렇게 $PSCmdlet.ParameterSetName으로 지금 어떤 집합으로 실행됐는지 판단해서 분기하면 돼요.

더 알아보기