about_Parameter_Sets — 파라미터 집합(Parameter Set) 다루기
about_Parameter_Sets — 파라미터 집합(Parameter Set) 다루기
파라미터 집합(parameter set)은 파워셸의 고급 함수(advanced function)를 쓸 때 가장 유용한 기능 중 하나예요. 하나의 함수를 여러 상황에서 다르게 동작하도록 만들고 싶을 때가 있죠. 예를 들어 어떤 입력을 받으면 파일 개수를 세고, 다른 입력을 받으면 내용의 줄 수를 셀 수 있도록요. 이렇게 하나의 함수가 시나리오에 따라 다른 동작을 하게 해 주는 장치가 파라미터 집합이에요. 사용자에게 노출되는 파라미터를 시나리오별로 다르게 보여 줄 수도 있고, 지정한 파라미터에 따라 돌려주는 결과도 달라질 수 있어요.
여기서 중요한 점이 하나 있어요. 한 번에 하나의 파라미터 집합만 사용할 수 있어요. 여러 집합을 동시에 쓰는 건 파워셸이 허용하지 않아요.
본문
파라미터 집합의 요구사항
모든 파라미터 집합에 공통으로 적용되는 규칙이 몇 가지 있어요. 먼저 이 규칙들을 짚고 넘어갈게요.
- 파라미터에 집합 이름을 지정하지 않으면, 그 파라미터는 모든 파라미터 집합에 속해요.
- 각 파라미터 집합은 고유한 파라미터 조합을 가져야 해요. 가능하면 그 고유 파라미터 중 적어도 하나는 필수(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 특성으로 제대로 참조할 수 없는 명시적 파라미터 집합을 만들어 버리게 되니 조심하세요.
예시: 파일 줄·문자·단어 수 세기
다음 예시 함수는 텍스트 파일의 줄 수, 문자 수, 단어 수를 세는 함수예요. 파라미터를 이용해서 어떤 값을 돌려받을지, 어떤 파일을 대상으로 할지를 지정할 수 있어요. 여기에는 네 개의 파라미터 집합이 정의돼 있어요.
PathPathAllLiteralPathLiteralPathAll
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
}
}
}
각 파라미터 집합은 고유한 파라미터 하나 또는 고유한 조합을 가져야 해요. Path와 PathAll 집합은 아주 비슷하지만, All 파라미터가 PathAll 집합에만 있어요. LiteralPath와 LiteralPathAll도 마찬가지예요. PathAll과 LiteralPathAll 둘 다 All 파라미터를 갖고 있지만, Path와 LiteralPath 파라미터가 이 둘을 구분해 주는 거예요.
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.
Path와 LiteralPath는 Get-ChildItem 커맨드릿의 서로 다른 파라미터 집합에 속하는 고유 파라미터예요. 이 둘을 같은 커맨드릿 호출에 함께 쓰면 오류가 나요. 커맨드릿 호출 한 번에는 파라미터 집합 하나만 쓸 수 있기 때문이에요.
어떤 파라미터 집합이 사용됐는지 알아내기
자동 변수 $PSCmdlet에 ParameterSetName 속성이 있어요. 이 속성에는 현재 사용 중인 파라미터 집합의 이름이 들어 있어요. 함수 안에서 이 속성을 이용하면 집합에 따라 다른 동작을 선택할 수 있어요.
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으로 지금 어떤 집합으로 실행됐는지 판단해서 분기하면 돼요.
더 알아보기
- about_Functions_CmdletBindingAttribute — CmdletBinding 특성과 기본 파라미터 집합 설정
- PowerShell 공식 문서 — PowerShell-Docs 저장소 — 이 문서의 원본과 변경 이력