about_Comparison_Operators — 비교 연산자
about_Comparison_Operators — 비교 연산자
PowerShell의 비교 연산자는 값을 비교하거나, 컬렉션에서 조건에 맞는 요소를 걸러내는 데 써요. 비교라는 게 처음 들어가면 "그냥 같냐 다르냐?" 정도로 생각하기 쉬운데, PowerShell에서는 스칼라 하나를 비교할 때와 컬렉션을 다룰 때 결과가 완전히 달라져요. 이 글에서는 비교 연산자의 종류와 각각의 쓰임을 하나씩 풀어볼게요.
본문
짧게 요약하면
PowerShell의 비교 연산자는 두 값을 비교하거나, 컬렉션의 요소를 입력값과 대조해서 걸러내는 역할을 해요.
자세한 설명
비교 연산자를 쓰면 값을 서로 비교하거나, 지정된 패턴에 맞는 값을 찾을 수 있어요. PowerShell이 제공하는 비교 연산자는 이렇게 정리할 수 있어요.
동등(Equality)
| 연산자 | 의미 |
|---|---|
-eq, -ieq, -ceq |
같다 |
-ne, -ine, -cne |
같지 않다 |
-gt, -igt, -cgt |
크다 |
-ge, -ige, -cge |
크거나 같다 |
-lt, -ilt, -clt |
작다 |
-le, -ile, -cle |
작거나 같다 |
매칭(Matching)
| 연산자 | 의미 |
|---|---|
-like, -ilike, -clike |
문자열이 와일드카드 패턴과 일치 |
-notlike, -inotlike, -cnotlike |
문자열이 와일드카드 패턴과 불일치 |
-match, -imatch, -cmatch |
문자열이 정규식 패턴과 일치 |
-notmatch, -inotmatch, -cnotmatch |
문자열이 정규식 패턴과 불일치 |
치환(Replacement)
| 연산자 | 의미 |
|---|---|
-replace, -ireplace, -creplace |
정규식 패턴에 맞는 문자열을 찾아 치환 |
포함(Containment)
| 연산자 | 의미 |
|---|---|
-contains, -icontains, -ccontains |
컬렉션이 값을 포함 |
-notcontains, -inotcontains, -cnotcontains |
컬렉션이 값을 포함하지 않음 |
-in, -iin, -cin |
값이 컬렉션 안에 있음 |
-notin, -inotin, -cnotin |
값이 컬렉션 안에 없음 |
형식(Type)
| 연산자 | 의미 |
|---|---|
-is |
두 객체가 같은 형식 |
-isnot |
두 객체가 같은 형식이 아님 |
공통 특징
문자열 비교는 명시적으로 대소문자 구분 연산자를 쓰지 않는 한 대소문자를 구분하지 않아요. 대소문자를 구분하고 싶다면 - 뒤에 c를 붙이면 돼요. 예를 들어 -ceq는 -eq의 대소문자 구분 버전이에요. 반대로 대소문자 무시를 명시적으로 드러내고 싶다면 - 뒤에 i를 붙여요. 예를 들어 -ieq는 -eq를 명시적으로 대소문자 무시로 쓴 버전이에요.
문자열 비교는 대소문자 구분 여부와 관계없이 InvariantCulture를 사용해요. 비교는 유니코드 코드 포인트 단위로 이뤄져서 문화권별 정렬 규칙은 쓰지 않아요. 그래서 현재 문화권이 무엇이든 결과는 같아요.
비교식에서 왼쪽 값이 스칼라(scalar)면 연산자는 Boolean 값을 반환해요. 반면 왼쪽 값이 컬렉션이면 연산자는 컬렉션에서 오른쪽 값과 일치하는 요소들을 반환해요. 오른쪽 값은 컬렉션이어도 항상 단일 값(singleton)으로 취급돼요. 비교 연산자는 컬렉션끼리 제대로 비교하지는 못해요.
컬렉션에서 일치하는 게 없으면 비교 연산자는 빈 배열을 반환해요. 예를 들어 볼게요.
$a = (1, 2) -eq 3
$a.GetType().Name
$a.Count
Object[]
0
다만 예외가 몇 가지 있어요.
- 포함(containment) 연산자와 형식(type) 연산자는 항상 Boolean 값을 반환해요.
-replace연산자는 치환 결과를 반환해요.-match와-notmatch연산자는 왼쪽이 컬렉션이 아닐 때$Matches자동 변수에도 값을 채워 넣어요.
동등 연산자
-eq와 -ne
왼쪽이 스칼라일 때 -eq는 오른쪽과 같으면 True, 아니면 False를 반환해요. -ne는 그 반대예요. 양쪽이 같으면 False, 같지 않으면 True예요.
2 -eq 2 # Output: True
2 -eq 3 # Output: False
'abc' -eq 'abc' # Output: True
'abc' -eq 'abc', 'def' # Output: False
'abc' -ne 'def' # Output: True
'abc' -ne 'abc' # Output: False
'abc' -ne 'abc', 'def' # Output: True
왼쪽이 컬렉션이면 -eq는 오른쪽 값과 일치하는 요소만 반환하고, -ne는 그런 요소를 걸러내요.
1,2,3 -eq 2 # Output: 2
'abc', 'def' -eq 'abc' # Output: abc
'abc', 'def' -ne 'abc' # Output: def
이 연산자들은 컬렉션의 모든 요소를 처리해요.
'zzz', 'def', 'zzz' -eq 'zzz'
zzz
zzz
동등 연산자는 서로 다른 형식의 객체도 비교할 수 있어요. 여기서 꼭 이해해야 할 점이 있는데, 비교할 때 오른쪽 값이 왼쪽 값의 형식으로 변환될 수 있다는 거예요.
예를 들어 문자열 '1.0'은 비교를 위해 정수로 변환돼서 값 1과 비교돼요. 아래 예시는 True를 반환해요.
PS> 1 -eq '1.0'
True
이번엔 반대로 값 1이 문자열로 변환돼 '1.0'과 비교돼요. 그래서 False를 반환해요.
PS> '1.0' -eq 1
False
동등 연산자는 스칼라나 컬렉션뿐 아니라 어떤 두 객체든 받아들이기는 해요. 다만 비교 결과가 사용자에게 항상 의미 있게 나온다는 보장은 없어요. 다음 예시가 바로 그 문제를 보여줘요.
class MyFileInfoSet {
[string]$File
[int64]$Size
}
$a = [MyFileInfoSet]@{File = 'C:\Windows\explorer.exe'; Size = 4651032}
$b = [MyFileInfoSet]@{File = 'C:\Windows\explorer.exe'; Size = 4651032}
$a -eq $b
False
이 예시에서는 프로퍼티가 완전히 똑같은 객체 두 개를 만들었어요. 그런데도 동등 비교 결과가 False예요. 서로 다른 객체이기 때문이죠. 비교 가능한 클래스를 만들려면 클래스에 System.IEquatable<T>를 구현해야 해요. 다음 예시는 File과 Size 두 프로퍼티를 가진 MyFileInfoSet 클래스에 System.IEquatable<T>를 부분 구현한 모습이에요. Equals() 메서드는 두 MyFileInfoSet 객체의 File, Size 프로퍼티가 같으면 True를 반환해요.
class MyFileInfoSet : System.IEquatable[Object] {
[string]$File
[int64]$Size
[bool] Equals([Object] $obj) {
return ($this.File -eq $obj.File) -and ($this.Size -eq $obj.Size)
}
}
$a = [MyFileInfoSet]@{File = 'C:\Windows\explorer.exe'; Size = 4651032}
$b = [MyFileInfoSet]@{File = 'C:\Windows\explorer.exe'; Size = 4651032}
$a -eq $b
True
아무 객체나 비교하는 대표적인 예로, 그 객체가 null인지 알아보는 경우가 있어요. 그런데 변수가 $null인지 확인하려면 반드시 $null을 왼쪽에 놓아야 해요. 오른쪽에 놓으면 생각한 대로 동작하지 않아요.
예를 들어 $a에 null 요소를 포함한 배열을 만들어 볼게요.
$a = 1, 2, $null, 4, $null, 6
아래는 $a가 null이 아닌지 테스트해요.
$null -ne $a
True
반면 아래는 $a에서 null 요소를 전부 걸러내요.
$a -ne $null # Output: 1, 2, 4, 6
1
2
4
6
-gt, -ge, -lt, -le
-gt, -ge, -lt, -le는 동작이 아주 비슷해요. 양쪽이 모두 스칼라면 두 값의 비교 결과에 따라 True 또는 False를 반환해요.
| 연산자 | 언제 True를 반환하나 |
|---|---|
-gt |
왼쪽 값이 더 클 때 |
-ge |
왼쪽 값이 더 크거나 같을 때 |
-lt |
왼쪽 값이 더 작을 때 |
-le |
왼쪽 값이 더 작거나 같을 때 |
아래 예시는 모두 True를 반환해요.
8 -gt 6 # Output: True
8 -ge 8 # Output: True
6 -lt 8 # Output: True
8 -le 8 # Output: True
참고 대부분의 프로그래밍 언어에서 크다 연산자는
>이에요. 그런데 PowerShell에서 이 문자는 리다이렉션에 쓰여요. 자세한 내용은 about_Redirection을 참고해 주세요.
왼쪽이 컬렉션이면 이 연산자들은 컬렉션의 각 요소를 오른쪽 값과 비교해요. 그리고 각자의 논리에 따라 요소를 남기거나 버려요.
$a=5, 6, 7, 8, 9
Write-Output 'Test collection:'
$a
Write-Output "`nMembers greater than 7"
$a -gt 7
Write-Output "`nMembers greater than or equal to 7"
$a -ge 7
Write-Output "`nMembers smaller than 7"
$a -lt 7
Write-Output "`nMembers smaller than or equal to 7"
$a -le 7
Test collection:
5
6
7
8
9
Members greater than 7
8
9
Members greater than or equal to 7
7
8
9
Members smaller than 7
5
6
Members smaller than or equal to 7
5
6
7
이 연산자들은 System.IComparable을 구현한 어떤 클래스와도 동작해요.
# Date comparison
[datetime]'2001-11-12' -lt [datetime]'2020-08-01' # True
# Sorting order comparison
'a' -lt 'z' # True; 'a' comes before 'z'
'macOS' -ilt 'MacOS' # False
'MacOS' -ilt 'macOS' # False
'macOS' -clt 'MacOS' # True; 'm' comes before 'M'
다음 예시는 미국 QWERTY 키보드에 있는 기호 중 'a'보다 뒤에 정렬되는 기호가 없다는 걸 보여줘요. 그런 기호를 전부 담은 집합을 -gt로 'a'와 비교하면 결과는 빈 배열이 돼요.
$a=' ','`','~','!','@','#','$','%','^','&','*','(',')','_','+','-','=',
'{','}','[',']',':',';','"','''','\','|','/','?','.','>',',','<'
$a -gt 'a'
# Output: Nothing
연산자 양쪽이 합리적으로 비교할 수 없는 값이면 이 연산자들은 비종료 오류(non-terminating error)를 발생시켜요.
매칭 연산자
매칭 연산자(-like, -notlike, -match, -notmatch)는 지정된 패턴에 맞거나 맞지 않는 요소를 찾아요. -like와 -notlike의 패턴은 와일드카드 표현식(*, ?, [ ] 포함)이고, -match와 -notmatch는 정규식(regex)을 받아요.
문법은 이렇게 생겼어요.
<string[]> -like <wildcard-expression>
<string[]> -notlike <wildcard-expression>
<string[]> -match <regular-expression>
<string[]> -notmatch <regular-expression>
이 연산자들의 입력이 스칼라 값이면 Boolean 값을 반환해요.
입력이 값의 컬렉션이면 컬렉션의 각 항목이 비교를 위해 문자열로 변환돼요. -match와 -notmatch는 각각 일치하는 멤버, 일치하지 않는 멤버를 반환해요. 반면 -like와 -notlike는 멤버를 문자열로 반환해요. -like와 -notlike가 컬렉션 멤버에 대해 반환하는 문자열은 비교에 사용된 문자열, 즉 멤버를 문자열로 캐스팅해 얻은 값이에요.
-like와 -notlike
-like와 -notlike는 -eq, -ne와 비슷하게 동작하지만, 오른쪽 값에 와일드카드가 들어간 문자열이 올 수 있어요.
'PowerShell' -like '*shell' # Output: True
'PowerShell' -notlike '*shell' # Output: False
'PowerShell' -like 'Power?hell' # Output: True
'PowerShell' -notlike 'Power?hell' # Output: False
'PowerShell' -like 'Power[p-w]hell' # Output: True
'PowerShell' -notlike 'Power[p-w]hell' # Output: False
'PowerShell', 'Server' -like '*shell' # Output: PowerShell
'PowerShell', 'Server' -notlike '*shell' # Output: Server
가장 좋은 결과를 얻으려면 -like와 -notlike의 오른쪽 값은 와일드카드 표현식이 담긴 문자열 리터럴이어야 해요. PowerShell은 와일드카드 표현식을 와일드카드 표현식 파서에 넘겨요. 와일드카드 문자(*, ?, [ ]) 중 하나를 문자 그대로 매칭하려면 백틱()으로 이스케이프해야 해요. 예를 들어 리터럴 ?를 매칭하려면 와일드카드 표현식에 `` ? ``를 써요. 확장 가능한 문자열(expandable string) 표현식을 쓰면 PowerShell이 와일드카드 파서에 넘기기 전에 문자열을 확장해서, 이스케이프되지 않은 문자들이 와일드카드로 전달돼버려요.
# Escaped literals in an expandable string
PS> "f`?`?"
f??
# Escaped literals in a literal string
PS> 'f`?`?'
f`?`?
# Comparison containing 2 wildcards
PS> 'f??' -like 'f??'
True
PS> 'for' -like 'f??'
True
# Comparison containing literal '?' characters
PS> 'f??' -like 'f`?`?'
True
PS> 'for' -like 'f`?`?'
False
-match와 -notmatch
-match와 -notmatch는 정규식을 사용해 왼쪽 값에서 패턴을 찾아요. 정규식은 이메일 주소, UNC 경로, 형식이 맞춰진 전화번호처럼 복잡한 패턴도 매칭할 수 있어요. 오른쪽 문자열은 정규식 규칙을 따라야 해요.
참고 제대로 설계되지 않았거나 악의적으로 만들어진 정규식은 서비스 거부(denial of service) 상황을 만들 수 있어요. 자세한 내용은 이 문서의 정규식 안전 섹션을 참고해 주세요.
스칼라 예시를 볼게요.
# Partial match test, showing how differently -match and -like behave
'PowerShell' -match 'shell' # Output: True
'PowerShell' -like 'shell' # Output: False
# Regex syntax test
'PowerShell' -match '^Power\w+' # Output: True
'bag' -notmatch 'b[iou]g' # Output: True
입력이 컬렉션이면 연산자는 컬렉션에서 일치하는 멤버들을 반환해요.
'PowerShell', 'Super PowerShell', 'Power's hell' -match '^Power\w+'
# Output: PowerShell
'Rhell', 'Chell', 'Mel', 'Smell', 'Shell' -match 'hell'
# Output: Rhell, Chell, Shell
'Bag', 'Beg', 'Big', 'Bog', 'Bug' -match 'b[iou]g'
#Output: Big, Bog, Bug
'Bag', 'Beg', 'Big', 'Bog', 'Bug' -notmatch 'b[iou]g'
#Output: Bag, Beg
-match와 -notmatch는 정규식 캡처 그룹을 지원해요. 스칼라 입력에서 실행할 때마다 -match 결과가 True이거나 -notmatch 결과가 False이면, 연산자는 $Matches 자동 변수를 덮어써요. $Matches는 항상 '0'이라는 키를 가진 Hashtable인데, 여기에는 매칭 전체가 저장돼요. 정규식에 캡처 그룹이 있으면 $Matches는 각 그룹에 해당하는 추가 키를 갖게 돼요.
여기서 주의할 점은 $Matches 해시테이블이 일치하는 패턴의 첫 번째 발생만 담는다는 거예요.
$string = 'The last logged on user was CONTOSO\jsmith'
$string -match 'was (?<domain>.+)\\(?<user>.+)'
$Matches
Write-Output "`nDomain name:"
$Matches.domain
Write-Output "`nUser name:"
$Matches.user
True
Name Value
---- -----
domain CONTOSO
user jsmith
0 was CONTOSO\jsmith
Domain name:
CONTOSO
User name:
jsmith
-match 결과가 False이거나 -notmatch 결과가 True인 경우, 또는 입력이 컬렉션인 경우에는 $Matches 자동 변수가 덮어써지지 않아요. 그래서 그 변수는 이전에 설정된 값을 담고 있을 거고, 변수가 설정된 적이 없다면 $null일 거예요. 이 연산자 중 하나를 실행한 뒤 $Matches를 참조할 때는 조건문을 써서 현재 연산자 호출로 변수가 설정됐는지 확인하는 걸 고려해 보세요.
if ('<version>1.0.0</version>' -match '<version>(.*?)</version>') {
$Matches
}
자세한 내용은 about_Regular_Expressions와 about_Automatic_Variables를 참고해 주세요.
치환 연산자
-replace 연산자는 -match처럼 정규식을 써서 패턴을 찾아요. 다만 -match와 달리, 찾은 부분을 다른 지정된 값으로 치환해요.
참고 제대로 설계되지 않았거나 악의적으로 만들어진 정규식은 서비스 거부 상황을 만들 수 있어요. 자세한 내용은 이 문서의 정규식 안전 섹션을 참고해 주세요.
정규식으로 치환하기
문법을 확인할게요.
<input> -replace <regular-expression>, <substitute>
이 연산자는 정규식을 사용해서 값의 전체 또는 일부를 지정된 값으로 치환해요. 파일 이름 바꾸기 같은 관리 작업에 많이 쓸 수 있어요. 예를 들어 다음 명령은 모든 .txt 파일의 확장자를 .log로 바꿔요.
Get-ChildItem *.txt | Rename-Item -NewName { $_.Name -replace '\.txt$','.log' }
기본적으로 -replace 연산자는 대소문자를 구분하지 않아요. 대소문자를 구분하려면 -creplace, 명시적으로 대소문자 무시를 쓰려면 -ireplace를 사용해요.
'book' -ireplace 'B', 'C' # Case insensitive
'book' -creplace 'B', 'C' # Case-sensitive; hence, nothing to replace
Cook
book
PowerShell 7.2부터 -replace 문장에서 왼쪽 피연산자가 문자열이 아니면 그 값을 문자열로 변환해요. PowerShell은 문화권에 영향받지 않는(culture-insensitive) 문자열 변환을 수행해요.
예를 들어 문화권이 프랑스어(fr)로 설정돼 있으면 값 1.2의 문화권 민감 문자열 변환 결과는 1,2예요.
PowerShell 7.2 이전:
PS> [cultureinfo]::CurrentCulture = 'fr'
PS> 1.2 -replace ','
12
PowerShell 7.2 이후:
PS> [cultureinfo]::CurrentCulture = 'fr'
PS> 1.2 -replace ','
1.2
정규식 대체(substitutions)
캡처 그룹과 대체 문법을 써서 정규식으로 텍스트를 동적으로 치환할 수도 있어요. <substitute> 문자열에서 캡처 그룹은 그룹 식별자 앞에 달러 기호($)를 붙여 참조할 수 있어요.
다음 예시에서 -replace 연산자는 DomainName\Username 형태의 사용자 이름을 받아서 Username@DomainName 형태로 바꿔요.
$SearchExp = '^(?<DomainName>[\w-.]+)\\(?<Username>[\w-.]+)$'
$ReplaceExp = '${Username}@${DomainName}'
'Contoso.local\John.Doe' -replace $SearchExp, $ReplaceExp
[email protected]
경고
$문자는 PowerShell과 정규식 양쪽에서 문법적 역할을 가져요.
- PowerShell에서 쌍따옴표 사이에서는 변수를 나타내고 하위식(subexpression) 연산자로 동작해요.
- Regex 검색 문자열에서는 줄의 끝을 나타내요.
- Regex 치환 문자열에서는 캡처된 그룹을 나타내요. 정규식을 홑따옴표 사이에 넣거나, 그 앞에 백틱(`)을 넣어 주세요.
예를 들어 볼게요.
$1 = 'Goodbye'
'Hello World' -replace '(\w+) \w+', "$1 Universe"
# Output: Goodbye Universe
'Hello World' -replace '(\w+) \w+', '$1 Universe'
# Output: Hello Universe
$$는 Regex에서 리터럴 $를 나타내요. 치환 문자열에서 $$를 쓰면 결과에 리터럴 $가 포함돼요.
'5.72' -replace '(.+)', '$ $1' # Output: $ 5.72
'5.72' -replace '(.+)', '$$$1' # Output: $5.72
'5.72' -replace '(.+)', '$$1' # Output: $1
더 자세한 내용은 about_Regular_Expressions와 Substitutions in Regular Expressions를 참고해 주세요.
컬렉션에서 치환하기
-replace 연산자의 <input>이 컬렉션이면 PowerShell은 컬렉션의 모든 값에 치환을 적용해요.
'B1','B2','B3','B4','B5' -replace 'B', 'a'
a1
a2
a3
a4
a5
스크립트블록으로 치환하기
PowerShell 6부터 -replace 연산자는 치환을 수행하는 스크립트블록도 받아들여요. 이 스크립트블록은 일치할 때마다 한 번씩 실행돼요.
<String> -replace <regular-expression>, {<Script-block>}
스크립트블록 안에서는 $_ 자동 변수를 써서 치환 대상 텍스트와 그 밖의 유용한 정보에 접근할 수 있어요. 이 변수의 클래스 형식은 System.Text.RegularExpressions.Match예요.
다음 예시는 세 자리 숫자의 각 시퀀스를 문자에 해당하는 값으로 치환해요. 스크립트블록은 치환해야 할 세 자리 숫자 묶음마다 실행돼요.
'072101108108111' -replace '\d{3}', {return [char][int]$_.Value}
Hello
포함 연산자
포함 연산자(-contains, -notcontains, -in, -notin)는 동등 연산자와 비슷한데, 입력이 컬렉션이어도 항상 Boolean 값을 반환한다는 점이 달라요. 이 연산자들은 첫 번째 일치를 감지하는 순간 비교를 멈추는 반면, 동등 연산자는 입력의 모든 멤버를 평가해요. 그래서 아주 큰 컬렉션에서는 동등 연산자보다 더 빨리 결과를 돌려줘요.
-contains와 -notcontains
<Collection> -contains <scalar-object>
<Collection> -notcontains <scalar-object>
이 연산자들은 집합이 특정 요소를 포함하는지 알려줘요. -contains는 오른쪽 값(scalar-object)이 집합의 요소 중 하나와 일치하면 True를 반환하고, -notcontains는 그 대신 False를 반환해요.
'abc', 'def' -contains 'def' # Output: True
'abc', 'def' -notcontains 'def' # Output: False
'Windows', 'PowerShell' -contains 'Shell' # Output: False
'Windows', 'PowerShell' -notcontains 'Shell' # Output: True
'abc', 'def', 'ghi' -contains 'abc', 'def' # Output: False
'abc', 'def', 'ghi' -notcontains 'abc', 'def' # Output: True
조금 더 복잡한 예시를 볼게요.
$DomainServers = 'ContosoDC1', 'ContosoDC2', 'ContosoFileServer',
'ContosoDNS', 'ContosoDHCP', 'ContosoWSUS'
$thisComputer = 'ContosoDC2'
$DomainServers -contains $thisComputer
# Output: True
오른쪽 피연산자가 컬렉션이면 이 연산자들은 왼쪽 컬렉션과 비교하기 전에 그 값을 문자열 표현으로 변환해요.
$a = 'abc', 'def'
'abc', 'def', 'ghi' -contains $a # Output: False
# The following statements are equivalent
$a, 'ghi' -contains $a # Output: True
'$a', 'ghi' -contains $a # Output: True
'abc def', 'ghi' -contains $a # Output: True
-in과 -notin
<scalar-object> -in <Collection>
<scalar-object> -notin <Collection>
-in과 -notin 연산자는 PowerShell 3에서 -contains와 -notcontains의 문법 반대 방향으로 도입됐어요. -in은 왼쪽 값 <scalar-object>가 컬렉션의 요소 중 하나와 일치하면 True를, -notin은 그 대신 False를 반환해요.
아래 예시들은 앞서 -contains, -notcontains에서 본 것과 같은 일을 -in, -notin으로 다시 쓴 거예요.
'def' -in 'abc', 'def' # Output: True
'def' -notin 'abc', 'def' # Output: False
'Shell' -in 'Windows', 'PowerShell' # Output: False
'Shell' -notin 'Windows', 'PowerShell' # Output: True
'abc', 'def' -in 'abc', 'def', 'ghi' # Output: False
'abc', 'def' -notin 'abc', 'def', 'ghi' # Output: True
조금 더 복잡한 예시를 볼게요.
$DomainServers = 'ContosoDC1', 'ContosoDC2', 'ContosoFileServer',
'ContosoDNS', 'ContosoDHCP', 'ContosoWSUS'
$thisComputer = 'ContosoDC2'
$thisComputer -in $DomainServers
# Output: True
왼쪽 피연산자가 컬렉션이면 이 연산자들은 오른쪽 컬렉션과 비교하기 전에 그 값을 문자열 표현으로 변환해요.
$a = 'abc', 'def'
$a -in 'abc', 'def', 'ghi' # Output: False
# The following statements are equivalent
$a -in $a, 'ghi' # Output: True
$a -in '$a', 'ghi' # Output: True
$a -in 'abc def', 'ghi' # Output: True
형식 비교
형식 비교 연산자(-is, -isnot)는 객체가 특정 형식인지 확인할 때 써요.
<object> -is <type-reference>
<object> -isnot <type-reference>
$a = 1
$b = '1'
$a -is [int] # Output: True
$a -is $b.GetType() # Output: False
$b -isnot [int] # Output: True
$a -isnot $b.GetType() # Output: True
정규식 안전
정규식 패턴을 매칭하는 데 필요한 비교 연산의 수는, 패턴에 교대(alternation) 구조가 많으면 특히 중첩된 선택적 수량자(nested optional quantifiers)가 있을 때지수적으로 늘어날 수 있어요. 예를 들어 ^(a+)+$라는 정규식 패턴은 "a" 문자가 하나 이상 들어 있는 완전한 문자열을 매칭하도록 설계됐어요. 정규식 엔진은 매칭이 실패했다고 결론 내리기 전에 데이터를 통과하는 가능한 모든 경로를 정규식으로 따라가야 하는데, 중첩된 괄호가 데이터를 지나는 추가 경로를 아주 많이 만들어내요.
경고 악의적인 사용자가 정규식에 입력을 제공해서 Denial-of-Service 공격, 흔히 ReDoS 공격이라고 하는 상황을 만들 수 있어요.
ReDoS 시나리오를 막으려면 시간 제한(timeout)을 지정한 [regex] 객체를 만들어요. 정규식 엔진은 시간 제한을 초과하면 매칭 검색을 중단해요. 예를 들어 다음 코드는 2초 시간 제한이 있는 regex 객체를 만들어요.
$pattern = '^(a+)+$'
$regexOptions = 'None'
$matchTimeout = [timespan]::FromSeconds(2)
$regex = [regex]::new($pattern, $regexOptions, $matchTimeout)
$regex.IsMatch('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaX')
2초가 지나면 정규식 엔진이 검색을 중단하고 다음 오류를 반환해요.
MethodInvocationException: Exception calling "IsMatch" with "1" argument(s):
"The Regex engine has timed out while trying to match a pattern to an input
string. This can occur for many reasons, including very large inputs or
excessive backtracking caused by nested quantifiers, back-references and other
factors."
[regex] 클래스와 정규식 성능에 대한 자세한 내용은 다음 문서를 참고해 주세요.
더 알아보기
- about_Booleans
- about_Operators
- about_Regular_Expressions
- about_Wildcards
- Compare-Object
- ForEach-Object
- Where-Object