본문 바로가기
빌드노트 검색

빌드노트

PowerShell 배열에 0이 있는데 -eq 조건문은 거짓이다

PowerShell 배열의 -eq는 Boolean 대신 일치한 원소를 반환한다. 0이나 빈 문자열 하나가 남으면 if는 거짓이고, 같은 값이 두 개 남으면 참이 된다. 실제 타입과 개수를 비교하고 존재 여부를 -contains 또는 결과 개수로 판별하는 방법을 설명한다.

PowerShell에서 $values에 0이 있는데 if ($values -eq 0)가 거짓 분기로 갈 수 있다. 왼쪽이 배열이면 -eq는 일치 여부를 나타내는 Boolean 대신 일치한 원소들을 반환한다. 그 결과에 0 하나만 남으면 if는 거짓으로 평가한다. 배열에 값이 있는지만 확인하려면 $values -contains 0을 쓰고, 골라낸 원소도 필요하면 결과의 개수를 검사한다.

다음은 실제 업무 스크립트가 아닌 합성 배열로 확인한 예제다. Windows의 PowerShell 7.6.6, .NET 10.0.12에서 실행했다. PowerShell 7이 설치된 터미널에서 pwsh -NoProfile로 새 셸을 열고, 아래 코드를 순서대로 입력하면 된다. 추가 파일이나 패키지는 필요 없다.

먼저 같은 0을 비교하되, 왼쪽이 숫자 하나일 때와 배열일 때의 결과 타입·원소 수를 확인한다.

$values = @(0, 7)
if ($values -eq 0) { 'found' } else { 'not found' }

$scalar = 0 -eq 0
$filtered = $values -eq 0
$scalar.GetType().FullName
$scalar
@($scalar).Count
$filtered.GetType().FullName
$filtered.Count
$filtered[0].GetType().FullName
$filtered[0]

출력은 다음과 같았다.

not found
System.Boolean
True
1
System.Object[]
1
System.Int32
0

0 -eq 0은 Boolean True 하나였다. 배열을 왼쪽에 둔 $values -eq 0은 원소 수가 1인 System.Object[]였고, 그 안의 원소는 정수 0이었다. 비교가 실패한 것이 아니라, 비교로 고른 값을 조건문이 다시 참·거짓으로 바꾼 것이다. 공식 비교 연산자 문서 (새 탭에서 열림)도 왼쪽이 스칼라인 경우와 컬렉션인 경우를 구분한다.

여기에 적용되는 배열의 Boolean 변환 규칙 (새 탭에서 열림)은 다음과 같다.

  • 원소가 없으면 거짓이다.
  • 원소가 하나면 그 원소의 Boolean 값에 따른다. 숫자 0과 빈 문자열 ''은 거짓이다.
  • 원소가 둘 이상이면 참이다.

따라서 if ($filtered)는 이 배열에서 “일치 원소가 하나라도 있는가”를 보장하는 검사가 아니다. 직접 실행한 [bool]$filtered도 False였으므로, [bool]을 붙이는 것만으로 해결되지 않았다.

존재 여부를 묻는 조건으로 바꾸기

존재 여부만 필요하면 -contains를 쓴다. 앞의 $values = @(0, 7)을 유지하고, 있는 값과 없는 값을 비교한 뒤 필터 결과를 재사용할 때의 조건도 확인한다.

$values -contains 0
$values -contains 99
($values -contains 0).GetType().FullName

$filtered = @($values -eq 0)
if ($filtered.Count -gt 0) { 'found' } else { 'not found' }
True
False
System.Boolean
found

-contains는 일치한 값 자체를 돌려주지 않고 Boolean을 반환한다. 그래서 찾는 원소가 0이어도 True다. 이 반환 규칙은 공식 문서의 포함 연산자 설명 (새 탭에서 열림)에서 확인할 수 있다.

필터로 고른 원소를 뒤에서도 쓸 때는 $filtered.Count -gt 0처럼 개수를 비교한다. 존재 여부만 한 줄로 쓰면 @($values -eq 0).Count -gt 0이다. 여기서 @(...)는 안의 출력을 배열로 모으는 구문이다. 0개나 1개인 경우도 배열로 받는다. 배열 하위 식 연산자 설명 (새 탭에서 열림)

이 개수 검사는 왼쪽이 배열인 필터식에 적용한다. 스칼라 비교인 0 -eq 99는 Boolean False 하나를 반환하므로, @(0 -eq 99).Count도 1이었다. 왼쪽이 단일 값이라면 원래의 Boolean 비교 결과를 그대로 조건에 쓴다.

빈 문자열 하나와 원소 없음은 표시가 같을 수 있다

같은 $values를 이번에는 문자열 배열로 바꾼다. 다음 코드는 빈 문자열을 찾은 결과와 아무것도 찾지 못한 결과를 개수로 구별하고, 일반 문자열을 찾은 경우도 비교한다.

$values = @('', 'ready')
$empty = $values -eq ''
$missing = $values -eq 'absent'
"empty=<$empty>; missing=<$missing>"
$empty.Count
$empty[0].Length
$missing.Count
if ($empty) { 'found' } else { 'not found' }
if ($values -eq 'ready') { 'found' } else { 'not found' }
$values -contains ''
@($values -eq '').Count -gt 0
empty=<>; missing=<>
1
0
0
not found
found
True
True

문자열 안에 넣어 표시하면 두 결과 모두 <>로 보였다. 하지만 $empty는 빈 문자열 하나를 담고 있어 개수가 1이고, 그 첫 문자열의 길이가 0이다. $missing은 원소 자체가 없어 개수가 0이다. 두 결과의 타입은 모두 System.Object[]였고, $empty의 첫 원소 타입은 System.String이었다. 화면이 비어 보인다는 사실만으로 일치 원소가 없다고 판단하면 안 된다.

ready를 찾았을 때는 비어 있지 않은 문자열 하나가 남아 참이었다. 빈 문자열을 찾았을 때는 원소가 있어도 거짓이었다. 반면 마지막 두 줄의 -contains와 개수 비교는 빈 문자열의 존재를 모두 True로 판별했다. 자기 코드에서도 반환된 값의 내용과 결과 개수를 함께 확인하면 이 차이를 구별할 수 있다.

0을 하나 더 넣으면 같은 조건문이 참이 된다

데이터의 중복 여부에 따라 증상이 사라지는지도 확인할 수 있다. 숫자 배열에 0을 하나 더 넣고 같은 필터 결과를 조건으로 사용한다.

$values = @(0, 0, 7)
$filtered = $values -eq 0
$filtered.Count
if ($filtered) { 'found' } else { 'not found' }
2
found

이번에는 0이 두 개 남았다. 원소가 둘 이상인 배열은 참이므로, 같은 -eq 0 조건이 참 분기로 갔다. 빈 문자열을 두 개 넣은 별도 대조에서도 결과 개수는 2, 조건문은 참이었다. 일치 원소가 하나일 때만 나타나는 거짓 평가를 중복 데이터가 가릴 수 있다. 존재 여부를 검사하는 호출부는 -contains나 결과 개수 비교로 고쳐야 한다.

이 실행은 정수만 있는 배열과 문자열만 있는 배열을 대상으로 했다. 실제 자동화 스크립트, 혼합 타입의 암시적 변환, $null 원소, 사용자 정의 객체의 동등성은 검사하지 않았다. Windows PowerShell 5.1과 다른 운영체제에서도 직접 실행하지 않았다. 결과가 다르다면 우선 비교식 왼쪽과 반환값의 .GetType().FullName, 결과의 개수를 확인해 이 예제와 입력 조건이 같은지 살핀다.

← 전체 글 보기