about_WQL
about_WQL
PowerShell에서 WMI 개체를 가져올 때 쓰는 쿼리 언어, WQL(Windows Management Instrumentation Query Language)을 다루는 문서예요. WQL의 기본 문법인 SELECT, WHERE, FROM부터 LIKE나 OR/AND 같은 연산자, null 값 검색과 escape 문자까지 하나씩 살펴볼게요.
본문
간단한 설명 (Short description)
PowerShell에서 WMI 개체를 가져오는 데 쓸 수 있는 WQL(Windows Management Instrumentation Query Language)을 설명해요.
자세한 설명 (Long description)
WQL은 WMI(Windows Management Instrumentation)의 쿼리 언어입니다. 말 그대로 WMI에서 정보를 가져올 때 쓰는 언어예요.
그런데 PowerShell에서 WMI 쿼리를 할 때 꼭 WQL을 써야 하는 건 아니에요. 대신 CIM cmdlet의 매개 변수를 쓸 수도 있습니다. WQL 쿼리는 일반적인 CIM 명령보다는 조금 더 빠르고, 그 성능 차이는 수백 대의 시스템에서 명령을 실행할 때 두드러져요. 다만 성공하는 WQL 쿼리를 작성하는 데 들이는 시간이 성능 향상보다 커지지 않도록 잘 판단해야 해요.
WQL을 쓰기 위해 필요한 기본 구문은 SELECT, WHERE, FROM 세 가지입니다.
WQL을 언제 쓸까요
WMI를 다룰 때, 특히 WQL을 쓸 때 잊지 말아야 할 점이 하나 있어요. 여러분은 지금 PowerShell도 함께 쓰고 있다는 거죠. WQL 쿼리가 예상대로 동작하지 않을 때는 WQL을 디버깅하느라 애쓰는 것보다 일반적인 PowerShell 명령을 쓰는 편이 더 쉬운 경우가 많아요.
쿼리가 아주 방대한 양의 데이터를 반환하는 게 아니라면, 같은 일을 해주는 PowerShell cmdlet이 이미 있는데 복잡한 WQL 쿼리를 완벽하게 다듬느라 몇 시간을 쓰는 건 거의 생산적이지 않아요.
SELECT 문 사용하기
일반적인 WMI 쿼리는 SELECT 문으로 시작하는데, WMI 클래스의 전체 속성이나 특정 속성을 가져옵니다. 클래스의 모든 속성을 선택하려면 별표(*)를 쓰고, FROM 키워드로 WMI 클래스를 지정해요.
SELECT 문의 형식은 다음과 같아요.
SELECT <property> FROM <WMI-class>
예를 들어 아래 SELECT 문은 Win32_BIOS WMI 클래스의 인스턴스에서 모든 속성(*)을 선택합니다.
SELECT * FROM Win32_BIOS
참고
PowerShell은 기본 개체 속성만 표시해요. 이 속성들은
Types.ps1xml파일에 정의되어 있죠. 추가 속성을 표시하려면Select-Objectcmdlet이나Format-*cmdlet을 사용하세요.
WMI 클래스의 특정 속성을 선택하려면 속성 이름을 SELECT와 FROM 키워드 사이에 넣으면 돼요. 아래 쿼리는 Win32_BIOS WMI 클래스에서 BIOS의 이름만 선택하고, 그 쿼리를 $queryName 변수에 저장해요.
SELECT Name FROM Win32_BIOS
속성을 여러 개 선택하려면 쉼표로 속성 이름을 구분합니다. 다음 WMI 쿼리는 Win32_BIOS 클래스의 이름과 버전을 선택하고, 쿼리를 $queryNameVersion 변수에 저장해요.
SELECT name, version FROM Win32_BIOS
WQL 쿼리 사용하기
PowerShell 명령에서 WQL 쿼리를 쓰는 방법은 두 가지예요.
- CIM cmdlet 사용하기
[wmisearcher]형식 가속기(type accelerator) 사용하기
Get-CimInstance cmdlet 사용하기
Get-CimInstance는 WMI 클래스를 포함한 CIM 호환 클래스의 인스턴스를 가져옵니다. PowerShell 3.0에서 도입된 CIM cmdlet은 WMI cmdlet과 같은 일을 해요. CIM cmdlet은 WS-Management(WSMan) 표준과 CIM(Common Information Model) 표준을 따르는데, 덕분에 Windows 컴퓨터뿐 아니라 다른 운영 체제를 실행하는 컴퓨터도 같은 방식으로 관리할 수 있어요.
다음 명령은 Get-CimInstance cmdlet으로 WQL 쿼리를 실행해요.
Get-WmiObject에서 쓸 수 있는 WQL 쿼리는 전부 Get-CimInstance에서도 쓸 수 있습니다.
Get-CimInstance -Query "SELECT * FROM Win32_BIOS"
SMBIOSBIOSVersion : S03KT39A
Manufacturer : LENOVO
Name : S03KT39A
SerialNumber : MJ0AETTX
Version : LENOVO - 1270
Get-CimInstance는 Get-WmiObject가 반환하는 ManagementObject 대신 CimInstance 개체를 반환하지만, 두 개체는 상당히 비슷해요.
PS> (Get-CimInstance -Query "SELECT * FROM Win32_BIOS").GetType().FullName
Microsoft.Management.Infrastructure.CimInstance
PS> (Get-WmiObject -Query "SELECT * FROM Win32_BIOS").GetType().FullName
System.Management.ManagementObject
[wmisearcher] 형식 가속기 사용하기
[wmisearcher] 형식 가속기는 WQL 문 문자열로부터 ManagementObjectSearcher 개체를 만들어 줍니다. ManagementObjectSearcher 개체에는 많은 속성과 메서드가 있는데, 가장 기초적인 메서드는 지정한 WMI 쿼리를 실행하고 결과 개체를 반환하는 Get 메서드예요.
[wmisearcher]를 쓰면 ManagementObjectSearcher .NET 클래스에 쉽게 접근할 수 있어요. 이 덕분에 WMI를 쿼리하고 쿼리가 수행되는 방식을 원하는 대로 구성할 수 있죠.
[wmisearcher] 형식 가속기를 쓰는 방법은 다음과 같아요.
- WQL 문자열을
ManagementObjectSearcher개체로 캐스팅합니다. ManagementObjectSearcher개체의Get메서드를 호출합니다.
예를 들어 아래 명령은 "모두 선택" 쿼리를 캐스팅해 $bios 변수에 저장한 다음, 그 변수에서 Get() 메서드를 호출해요.
$bios = [wmisearcher]"SELECT * FROM Win32_BIOS"
$bios.Get()
SMBIOSBIOSVersion : 8BET56WW (1.36 )
Manufacturer : LENOVO
Name : Default System BIOS
SerialNumber : R9FPY3P
Version : LENOVO - 1360
[wmisearcher] 형식 가속기는 쿼리나 변수 어느 쪽에 캐스팅해도 돼요. 다음 예시에서는 형식 가속기로 변수를 캐스팅했는데, 결과는 같아요.
[wmisearcher]$bios = "SELECT * FROM Win32_BIOS"
$bios.Get()
SMBIOSBIOSVersion : S03KT39A
Manufacturer : LENOVO
Name : S03KT39A
SerialNumber : MJ0AETTX
Version : LENOVO - 1270
[wmisearcher] 형식 가속기를 쓰면 쿼리 문자열이 ManagementObjectSearcher 개체로 바뀌어요. 다음 명령으로 그 변화를 확인할 수 있어요.
$a = "SELECT * FROM Win32_BIOS"
$a.GetType().FullName
System.String
$a = [wmisearcher]"SELECT * FROM Win32_BIOS"
$a.GetType().FullName
System.Management.ManagementObjectSearcher
이 명령 형식은 어떤 쿼리에도 적용돼요. 다음 명령은 Win32_BIOS WMI 클래스의 Name 속성 값을 가져오죠.
$biosName = [wmisearcher]"Select Name from Win32_BIOS"
$biosName.Get()
__GENUS : 2
__CLASS : Win32_BIOS
__SUPERCLASS :
__DYNASTY :
__RELPATH :
__PROPERTY_COUNT : 1
__DERIVATION : {}
__SERVER :
__NAMESPACE :
__PATH :
Name : S03KT39A
PSComputerName :
기본 WQL WHERE 문 사용하기
WHERE 문은 SELECT 문이 반환할 데이터에 조건을 걸어주는 역할을 해요.
WHERE 문의 형식은 다음과 같아요.
WHERE <property> <operator> <value>
예를 들면 이렇게요.
WHERE Name = 'Notepad.exe'
WHERE 문은 SELECT 문과 함께 쓰여요. 다음 예시를 볼게요.
SELECT * FROM Win32_Process WHERE Name = 'Notepad.exe'
WHERE 문을 쓸 때는 속성 이름과 값이 정확해야 해요. 예를 들어 다음 명령은 로컬 컴퓨터의 Notepad 프로세스를 가져옵니다.
Get-CimInstance -Query "SELECT * FROM Win32_Process WHERE name='Notepad.exe'"
그런데 다음 명령은 실패해요. 프로세스 이름에 .exe 파일 확장자가 포함되어 있기 때문이죠.
Get-CimInstance -Query "SELECT * FROM Win32_Process WHERE name='Notepad'"
WHERE 문 비교 연산자
WQL WHERE 문에서 유효한 연산자는 다음과 같아요.
| 연산자 | 설명 |
|---|---|
= |
같음 (Equal) |
!= |
같지 않음 (Not equal) |
<> |
같지 않음 (Not equal) |
< |
작음 (Less than) |
> |
큼 (Greater than) |
<= |
작거나 같음 (Less than or equal) |
>= |
크거나 같음 (Greater than or equal) |
LIKE |
와일드카드 일치 (Wildcard match) |
IS |
null 평가 (Evaluates null) |
ISNOT |
null이 아님 평가 (Evaluates not null) |
ISA |
WMI 클래스의 멤버 평가 (Evaluates a member of a WMI class) |
이 외에도 연산자가 더 있지만, 비교를 할 때 쓰는 것은 이 정도예요.
예를 들어 다음 쿼리는 Win32_Process 클래스에서 프로세스 우선순위가 11 이상인 프로세스의 Name과 Priority 속성을 선택해요. 쿼리는 Get-CimInstance cmdlet으로 실행하죠.
$highPriority = "Select Name, Priority from Win32_Process " +
"WHERE Priority >= 11"
Get-CimInstance -Query $highPriority
-Filter 매개 변수에서 WQL 연산자 사용하기
WQL 연산자는 Get-CimInstance cmdlet의 -Filter 매개 변수 값은 물론, 이 cmdlet들의 Query 매개 변수 값에서도 쓸 수 있어요.
예를 들어 다음 명령은 ProcessId 값이 1004보다 큰 마지막 다섯 개 프로세스의 Name과 ProcessId 속성을 가져옵니다. ProcessId 조건은 -Filter 매개 변수로 지정했어요.
$getCimInstance = @{
Class = 'Win32_Process'
Property = 'Name', 'ProcessId'
Filter = "ProcessId >= 1004"
}
Get-CimInstance @getCimInstance |
Sort-Object ProcessId |
Select-Object Name, ProcessId -Last 5
Name ProcessId
---- ---------
SROSVC.exe 4220
WINWORD.EXE 4664
TscHelp.exe 4744
SnagIt32.exe 4748
WmiPrvSE.exe 5056
LIKE 연산자 사용하기
LIKE 연산자는 와일드카드 문자를 사용해 WQL 쿼리 결과를 필터링하게 해줘요.
| Like 연산자 | 설명 |
|---|---|
[] |
범위 안의 문자 [a-f] 또는 문자 집합 [abcdef]. 집합의 항목은 연속되거나 알파벳순일 필요가 없어요. |
^ |
범위에 없는 문자 [^a-f] 또는 집합에 없는 문자 [^abcdef]. 집합의 항목은 연속되거나 알파벳순일 필요가 없어요. |
% |
0개 이상의 문자로 이루어진 문자열 |
_ |
한 문자 (밑줄). 쿼리 문자열에서 밑줄을 리터럴로 쓰려면 대괄호로 감싸 [_]로 표기하면 돼요. |
LIKE 연산자를 와일드카드나 범위 연산자 없이 쓰면 같음 연산자(=)처럼 동작해서, 패턴과 정확히 일치하는 개체만 반환해요.
범위 연산자와 백분율(%) 와일드카드를 조합하면 단순하면서도 강력한 필터를 만들 수 있어요.
LIKE 연산자 예시
예시 1: [<range>]
다음 명령은 Notepad를 시작한 뒤, 이름이 "H"와 "N" 사이의 문자로 시작하는(대소문자 구분 없음) Win32_Process 클래스 인스턴스를 검색해요. 쿼리는 Hotepad.exe부터 Notepad.exe까지의 프로세스를 반환해야 하죠.
Notepad # Starts Notepad
$query = "SELECT * FROM Win32_Process WHERE Name LIKE '[H-N]otepad.exe'"
Get-CimInstance -Query $query | select Name, ProcessId
Name ProcessId
---- ---------
notepad.exe 1740
예시 2: [<range>]와 %
다음 명령은 이름이 A와 P 사이의 문자(대소문자 구분 없음)로 시작하고, 그 뒤에 어떤 조합의 문자가 0개 이상 따라오는 모든 프로세스를 선택해요.
Get-CimInstance cmdlet이 쿼리를 실행하고, Select-Object cmdlet이 Name과 ProcessId 속성을 가져오며, Sort-Object cmdlet이 결과를 이름의 알파벳순으로 정렬합니다.
$query = "SELECT * FROM Win32_Process WHERE name LIKE '[A-P]%'"
Get-CimInstance -Query $query |
Select-Object -Property Name, ProcessId |
Sort-Object -Property Name
예시 3: 범위 밖 (^)
다음 명령은 이름이 A, S, W, P, R, C, U, N 중 어떤 문자로도 시작하지 않으면서 뒤에 0개 이상의 문자가 따르는 프로세스를 가져와요.
$query = "SELECT * FROM Win32_Process WHERE name LIKE '[^ASWPRCUN]%'"
Get-CimInstance -Query $query |
Select-Object -Property Name, ProcessId |
Sort-Object -Property Name
예시 4: 모든 문자 또는 없음 (%)
다음 명령은 이름이 calc로 시작하는 프로세스를 가져옵니다. 백분율 기호(%)는 WQL의 와일드카드 문자예요. PowerShell의 별표(*) 와일드카드에 해당하죠.
$query = "SELECT * FROM Win32_Process WHERE Name LIKE 'calc%'"
Get-CimInstance -Query $query | Select-Object -Property Name, ProcessId
Name ProcessId
---- ---------
calc.exe 4424
예시 5: 한 문자 (_)
다음 명령은 c_lc.exe 패턴의 이름을 가진 프로세스를 가져옵니다. 여기서 밑줄 문자는 한 글자 아무거나를 나타내요. 이 패턴은 calc.exe부터 czlc.exe, c9lc.exe까지의 이름과 일치하지만, "c"와 "l" 사이에 문자가 두 개 이상 떨어진 이름은 일치하지 않아요.
$query = "SELECT * FROM Win32_Process WHERE Name LIKE 'c_lc.exe'"
Get-CimInstance -Query $query | Select-Object -Property Name, ProcessId
Name ProcessId
---- ---------
calc.exe 4424
예시 6: 정확히 일치
다음 명령은 WLIDSVC.exe라는 이름의 프로세스를 가져와요. 쿼리가 LIKE 키워드를 쓰고 있지만 값에 와일드카드 문자가 없으므로 정확히 일치해야 해요.
$query = "SELECT * FROM Win32_Process WHERE name LIKE 'WLIDSVC.exe'"
Get-CimInstance -Query $query | Select-Object -Property Name, ProcessId
Name ProcessId
---- ---------
WLIDSVC.exe 84
OR 연산자 사용하기
서로 독립적인 조건을 여러 개 지정하려면 OR 키워드를 써요. OR 키워드는 WHERE 절에 등장하죠. 두 개 이상의 조건에 대해 포함적 OR 연산(inclusive OR)을 수행해서, 조건 중 어느 하나라도 충족하는 항목을 반환합니다.
OR 연산자의 형식은 다음과 같아요.
WHERE <property> <operator> <value> OR <property> <operator> <value> ...
예를 들어 다음 명령은 Win32_Process WMI 클래스의 모든 인스턴스를 가져오되, 프로세스 이름이 winword.exe나 excel.exe인 것만 반환해요.
$q = "SELECT * FROM Win32_Process WHERE Name='winword.exe'" +
" OR Name='excel.exe'"
Get-CimInstance -Query $q
OR 문은 두 개 이상의 조건에도 쓸 수 있어요. 다음 쿼리에서는 OR 문으로 Winword.exe, Excel.exe, powershell.exe를 가져옵니다.
$q = "SELECT * FROM Win32_Process WHERE Name='winword.exe'" +
" OR Name='excel.exe' OR Name='powershell.exe'"
AND 연산자 사용하기
서로 연관된 조건을 여러 개 지정하려면 AND 키워드를 써요. AND 키워드는 WHERE 절에 등장하며, 모든 조건을 충족하는 항목만 반환합니다.
AND 연산자의 형식은 다음과 같아요.
WHERE <property> <operator> <value> `AND` <property> <operator> <value> ...
예를 들어 다음 명령은 이름이 Winword.exe이고 프로세스 ID가 6512인 프로세스를 가져와요. 이 명령들이 Get-CimInstance cmdlet을 쓴다는 점을 기억하세요.
$q = "SELECT * FROM Win32_Process WHERE Name = 'winword.exe' " +
"AND ProcessId =6512"
Get-CimInstance -Query $q
ProcessId Name HandleCount WorkingSetSize VirtualSize
--------- ---- ----------- -------------- -----------
# 6512 WINWORD.EXE 768 117170176 633028608
LIKE 연산자를 포함한 모든 연산자는 OR 연산자와 AND 연산자와 함께 쓸 수 있어요. 또 단일 쿼리에서 OR와 AND 연산자를 괄호로 묶어 조합하면, WMI가 어느 절을 먼저 처리할지 지정할 수 있습니다.
이 명령은 PowerShell의 줄 연속 문자(`)를 써서 명령을 두 줄로 나누고 있어요.
null 값 검색하기
WMI에서 null 값을 검색하는 일은 꽤 까다롭습니다. 예측할 수 없는 결과를 낳을 수 있기 때문이에요. null은 0도 아니고 빈 문자열과도 같지 않아요. 어떤 WMI 클래스 속성은 초기화되고 어떤 속성은 초기화되지 않기 때문에, null 검색이 모든 속성에서 동작하지 않을 수 있어요.
null 값을 검색하려면 Is 연산자를 값 null과 함께 써요.
예를 들어 다음 명령은 InstallDate 속성 값이 null인 프로세스를 가져옵니다. 이 명령은 많은 프로세스를 반환해요.
$q = "SELECT * FROM Win32_Process WHERE InstallDate is null"
Get-CimInstance -Query $q
반대로 다음 명령은 Description 속성 값이 null인 사용자 계정을 가져와요. 대부분의 사용자 계정이 Description 속성에 값이 없음에도, 이 명령은 어떤 사용자 계정도 반환하지 않죠.
$q = "SELECT * FROM Win32_UserAccount WHERE Description is null"
Get-CimInstance -Query $q
Description 속성에 값이 없는 사용자 계정을 찾으려면 같음 연산자로 빈 문자열을 검색하면 돼요. 빈 문자열을 나타내려면 작은따옴표를 두 개 연달아 씁니다.
$q = "SELECT * FROM Win32_UserAccount WHERE Description = '' "
true 또는 false 사용하기
WMI 개체 속성에서 부울 값을 가져오려면 True와 False를 쓰면 돼요. 이 값은 대소문자를 구분하지 않아요.
다음 WQL 쿼리는 도메인에 가입된 컴퓨터에서 로컬 사용자 계정만 반환합니다.
$q = "SELECT * FROM Win32_UserAccount WHERE LocalAccount = True"
Get-CimInstance -Query $q
도메인 계정을 찾으려면 다음 예시처럼 값으로 False를 쓰면 돼요.
$q = "SELECT * FROM Win32_UserAccount WHERE LocalAccount = False"
Get-CimInstance -Query $q
escape 문자 사용하기
WQL은 escape 문자로 백슬래시(\)를 씁니다. 이것은 백틱 문자(`)를 쓰는 PowerShell과 다르다는 점을 기억하세요.
따옴표와, 따옴표를 나타내는 데 쓰는 문자들은 잘못 해석되지 않도록 escape 처리해야 하는 경우가 많아요.
이름에 작은따옴표가 들어간 사용자를 찾으려면 다음 명령처럼 백슬래시로 작은따옴표를 escape 하세요.
$q = "SELECT * FROM Win32_UserAccount WHERE Name = 'Tim O\'Brian'"
Get-CimInstance -Query $q
Name Caption AccountType SID Domain
---- ------- ----------- --- ------
Tim O'Brian FABRIKAM\TimO 512 S-1-5-21-1457... FABRIKAM
어떤 경우에는 백슬래시 자체도 escape 해야 해요. 예를 들어 다음 명령은 Caption 값의 백슬래시 때문에 Invalid Query 오류를 일으킵니다.
$q = "SELECT * FROM Win32_UserAccount WHERE Caption = 'Fabrikam\TimO'"
Get-CimInstance -Query $q
Get-CimInstance : Invalid query
At line:1 char:1
+ Get-CimInstance -Query $q
+ ~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Get-CimInstance], CimExcep
+ FullyQualifiedErrorId : HRESULT 0x80041017,Microsoft.Management.Infrastr
백슬래시를 escape 하려면 다음 명령처럼 백슬래시를 하나 더 붙이면 돼요.
$q = "SELECT * FROM Win32_UserAccount WHERE Caption = 'Fabrikam\\TimO'"
Get-CimInstance -Query $q