본문으로 건너뛰기

PowerShell 다시 배우기 7 - 배열과 해시 테이블

PowerShell 다시 배우기 7/11

배열

쉼표로 늘어놓으면 배열이다. 빈 배열이나 원소가 하나인 배열은 @()로 만든다.

$numbers = 10, 20, 30, 40, 50
$empty   = @()
$one     = @('only')

명령의 결과는 하나일 수도 여럿일 수도 있다. 결과가 하나면 배열이 아니라 그 개체 하나가 돌아온다. 늘 배열로 다루고 싶으면 @()로 감싼다.

$files = @(Get-ChildItem *.log)
$files.Count    # 0개, 1개, 여러 개 어느 때나 맞는 값

꺼내고 자르기

번호는 0부터 세고, 음수는 뒤에서부터 센다. 범위나 여러 번호를 주면 잘라 낸다.

$numbers[0]         # 10
$numbers[-1]        # 50
$numbers[1..3]      # 20 30 40
$numbers[-2..-1]    # 40 50
$numbers[0, 2, 4]   # 10 30 50

파이프라인 없이 거르고 바꾸는 메서드도 있다. 메모리에 이미 있는 배열에는 이쪽이 빠르다.

(1..10).Where({ $_ % 2 -eq 0 })    # 2 4 6 8 10
(1..3).ForEach({ $_ * 2 })         # 2 4 6

+= 는 느리다

배열은 크기가 고정이다. +=는 원소를 덧붙이는 것이 아니라 하나 더 큰 배열을 새로 만들어 전부 복사한다. 몇 개일 때는 상관없지만 반복문 안에서 수만 번 하면 차이가 크다. 숫자 2만 개를 모으는 데 걸린 시간을 재 봤다(PowerShell 7.6.6, 라즈베리 파이 5).

방법걸린 시간
$a += $i1,275ms
List[int]의 .Add()64ms
반복문의 결과를 변수에 받기42ms

7.5에서 +=가 많이 빨라졌지만 여전히 가장 느리다. 가장 빠르고 PowerShell다운 방법은 반복문이 내놓는 값을 그대로 받는 것이다.

$result = foreach ($i in 1..20000) { $i * 2 }

중간에 넣고 빼야 하면 리스트를 쓴다.

$list = [System.Collections.Generic.List[string]]::new()
$list.Add('a')
$list.Remove('a')

해시 테이블

키와 값의 쌍이다. @{}로 만든다.

$server = @{
    Name = 'web1'
    Port = 443
}

$server.Name            # web1
$server['Port']         # 443
$server.Region = 'kr'   # 추가
$server.Remove('Port')  # 삭제
$server.ContainsKey('Name')

키와 값을 돌 때는 GetEnumerator()를 쓴다.

foreach ($entry in $server.GetEnumerator()) {
    "$($entry.Key) = $($entry.Value)"
}

순서가 필요하면 [ordered]

보통의 해시 테이블은 넣은 순서를 지키지 않는다. 순서가 중요하면 [ordered]를 붙인다.

$h = [ordered]@{ Name = 'talsu'; Lang = 'PowerShell' }
$h.Year = 2026
$h.Keys    # Name Lang Year

해시 테이블과 개체

해시 테이블 앞에 [pscustomobject]를 붙이면 개체가 된다. 표로 보이고, 정렬하고, CSV로 내보낼 수 있다.

[pscustomobject]$h
Name  Lang       Year
----  ----       ----
talsu PowerShell 2026

둘을 가르는 기준은 이렇다. 찾아보는 용도(이름으로 값을 꺼낸다)면 해시 테이블, 한 건의 기록(여러 건을 모아 표로 다룬다)이면 개체.

JSON은 양쪽으로 오간다. -AsHashtable을 붙이면 개체 대신 해시 테이블로 받는다. 키에 공백이나 특수 문자가 든 JSON을 다룰 때 편하다.

$config = Get-Content ./config.json -Raw | ConvertFrom-Json -AsHashtable
$config | ConvertTo-Json -Depth 5 | Set-Content ./config.json

ConvertTo-Json은 기본으로 두 단계까지만 풀어 쓴다. 더 깊은 자료는 -Depth를 늘리지 않으면 잘린다.

묶어서 찾아보기

Group-Object -AsHashTable은 목록을 키로 찾아볼 수 있는 표로 바꾼다. 같은 목록을 여러 번 뒤져야 할 때 매번 Where-Object로 거르는 것보다 훨씬 빠르다.

$byExtension = Get-ChildItem $PSHOME -File | Group-Object Extension -AsHashTable
$byExtension['.json'].Name

스플래팅: 매개변수를 해시 테이블로

매개변수가 많아 줄이 길어지면 해시 테이블에 담아 @로 넘긴다.

$params = @{
    Path   = $PSHOME
    Filter = '*.json'
    File   = $true
}
Get-ChildItem @params

줄을 잇는 백틱(`)을 쓰지 않아도 되고, 조건에 따라 매개변수를 넣고 빼기 쉽다.

if ($Recurse) { $params.Recurse = $true }
Get-ChildItem @params

다음 글: 함수와 매개변수

이 글을 가리키는 글