PowerShell 명령의 기초 — 가장 먼저 익힐 조작과 안전한 사용법

· 업데이트: · · PowerShell, Windows, 명령줄, 자동화, 기존 자산 활용

수정 이력(1건, 최종 수정 2026년 09월 03일)

이 글에 적용한 변경 사항의 기록입니다. 보관해 둔 수정 전 버전은 DOI가 부여된 고정 URL에서 읽을 수 있습니다.

한국어 전면 재작성에 맞춰 본문 표현을 바로잡았습니다. 기술적인 주장은 일본어판과 같습니다.
최초 공개
이 글을 인용하기(DOI: 10.5281/zenodo.21635282)

이 글은 Zenodo에 보관되어 있습니다. 항상 최신 버전으로 연결되는 DOI와 지금 보고 있는 버전에 고정된 DOI를 아래에 함께 제시합니다.

小村 豪 (2026). 「PowerShell 명령의 기초 — 가장 먼저 익힐 조작과 안전한 사용법」. 합동회사 코무라소프트. https://doi.org/10.5281/zenodo.21635282 https://comcomponent.com/ko/blog/powershell-command-basics/

DOI(최신 버전)
10.5281/zenodo.21635282
DOI(이 버전)
10.5281/zenodo.21635283

1. 가장 먼저 짚어야 할 것

PowerShell은 Windows 설정 확인, 파일 정리, 로그 조사, CSV 가공, 서비스 조작, 반복 작업의 자동화에 쓰는 명령 환경입니다.

다만 처음부터 복잡한 스크립트를 작성할 필요는 없습니다. 먼저 다음 흐름만 익히면 실무에서 꽤 쓸 수 있게 됩니다.

찾는다  본다  좁힌다  정렬한다  출력한다  필요하면 변경한다

PowerShell의 기본은 ‘긴 명령을 외우는 것’이 아닙니다. 중요한 것은 다음 세 가지입니다.

  1. Get-CommandGet-Help로 명령을 조사할 수 있을 것
  2. | 파이프라인으로 결과를 다음 명령에 넘길 수 있을 것
  3. 삭제·정지·변경 전에 -WhatIf-Confirm으로 영향을 확인할 것

PowerShell은 ‘조사’와 ‘변경’을 같은 화면에서 실행할 수 있습니다. 편리한 반면 삭제나 정지도 바로 실행할 수 있으므로, 읽기 전용 확인부터 시작하는 습관이 중요합니다.

이 글에 나오는 코드는 장 구성에 맞춘 주제별 스크립트 모음(임시 폴더에 연습용 워크스페이스를 만들어 안전하게 시험해 볼 수 있는 구성)과 Pester 테스트를 포함한 샘플 일식으로 GitHub에 공개되어 있습니다.

powershell-command-basics - komurasoft-blog-samples (GitHub)

이 글을 읽는 방법

이 글은 26장입니다. 처음부터 이어서 읽을 필요는 없습니다. 목적별로 다음 장부터 읽으면 헤매지 않습니다.

목적 읽을 장
PowerShell을 처음 다룬다 1~8장(사고방식, 조사 방법, 파이프라인, 파일 조작)과 24장(익히는 순서)
로그나 설정을 조사하고 싶다 9장(텍스트), 12장(프로세스·서비스), 13장(이벤트 로그), 21장(조사 샘플)
CSV나 JSON을 가공하고 싶다 10장, 11장, 15장
처리를 스크립트로 모으고 싶다 14장, 16장, 17장, 18장
삭제·이동을 안전하게 실행하고 싶다 19장, 22장, 25장
명령만 찾고 싶다 20장(기본 명령 목록), 23장(자주 하는 실수)

익히는 순서 자체는 24장 ‘명령을 익히는 순서’에 정리했습니다. 처음 손으로 따라 할 때는 24장의 제1단계(Get-Command, Get-Help, Get-Member, Get-Location, Set-Location, Get-ChildItem, Get-Content, Select-String)만 옆에 두고 본문을 따라가십시오. 이 여덟 가지를 알면, 나머지는 필요할 때 조사하면 됩니다.

‘실용 명령 모음’과의 역할 나누기

PowerShell 글은 한 편 더 있습니다. 역할이 다르므로 다음처럼 나눠 쓰십시오.

다루는 내용 읽는 시기
이 글(기본) 조작 체계. 명령을 찾는 방법, 파이프라인의 개념, 변경 명령을 안전하게 실행하는 순서 처음에 읽는다
PowerShell 실용 명령 모음 목적별 부품. 건수나 합계 집계(Measure-Object), 종류별 집계(Group-Object), 차이 비교(Compare-Object), 작업 로그 기록(Start-Transcript) 등 실제 작업에서 ‘이것을 하고 싶다’가 나왔을 때

CSV, 프로세스, 서비스, 이벤트 로그, Format-*의 주의점은 양쪽 글에 나옵니다. 이 글에서는 ‘왜 그 형태로 쓰는지’를, 실용 명령 모음에서는 ‘그 장면에서 쓰는 부품’을 다룹니다.

이 글의 지식 맵

PowerShell은 명령 결과를 문자열이 아니라 객체로 다루는 셸이며, Get-Command·Get-Help·Get-Member로 알아보는 방법과 Where-Object·Select-Object를 조합한 파이프라인이 기본이 됩니다. 화면 표시용 Format-* 계열 cmdlet은 맨 마지막에 써야 하며, Export-Csv나 ConvertTo-Json 앞에 끼워 넣으면 CSV·JSON 출력에 표시용 정보가 섞입니다. .ps1 스크립트를 실행하려면 실행 정책의 허용이 필요하고, 회사 PC에서는 그룹 정책으로 제어되는 경우가 있습니다. 삭제·중지·변경과 같은 변경 계열 명령은 Where-Object로 대상을 좁히고 Export-Csv로 증적을 남긴 다음, -WhatIf로 예행한 뒤에 실행하는 안전한 절차를 철저히 지키는 것이 이 글 전체의 결론입니다.

PowerShell 명령의 기본 지식 맵PowerShell의 객체 지향 파이프라인, Get-Command·Get-Help·Get-Member로 알아보는 방법, Format 계열 cmdlet과 CSV·JSON 출력의 관계, 실행 정책과 그룹 정책의 관계, 안전한 변경 절차에서 -WhatIf가 차지하는 위치를 보여주는 그림입니다.이용한다이용한다이용한다이용한다보다 먼저 해야 한다보다 먼저 해야 한다사용은 비권장이용한다이용한다이용한다에서 구성할 수 있다전제로 한다이용한다이용한다이용한다이용한다이용한다이용한다이용한다이용한다PowerShell파이프라인안전한 변경 절차(보기→좁히기→기록→예행→실행)Where-ObjectSelect-ObjectGet-MemberGet-CommandGet-HelpFormat-* 계열 커맨드릿Import-Csv/Export-CsvConvertTo-Json/ConvertFrom-JsonPowerShell 실행 정책그룹 정책.ps1 스크립트 파일-WhatIf 파라미터Get-WinEvent

그림의 실선은 항상 성립하는 관계, 점선은 조건이 붙는 관계입니다(성립 조건은 상세 페이지의 관계별 설명에 적혀 있습니다). 관계 전체 목록(총 20건, 근거와 확신도 포함)과 주요 개념의 정의는 지식 맵 상세 페이지에 정리되어 있습니다(일본어). 데이터: JSON-LD / Turtle

2. PowerShell과 명령 프롬프트의 차이

PowerShell은 기존의 명령 프롬프트(cmd.exe)와 비슷해 보이지만, 내부의 사고방식이 다릅니다.

관점 명령 프롬프트 PowerShell
주된 출력 문자열 객체
명령 이름 dir, copy Get-ChildItem, Copy-Item
결과 가공 문자열 처리가 중심 속성으로 좁히고 정렬
자동화 배치 파일 .ps1 스크립트
잘 다루는 분야 오래된 명령 자산과의 호환 Windows 관리, CSV, JSON, API, 반복 작업

예를 들어 파일 목록을 가져올 때, cmd.exe에서는 dir의 표시 결과를 문자열로 보는 경우가 많습니다. PowerShell에서는 파일이 Name, Length, LastWriteTime 같은 속성을 가진 객체로 다루어집니다.

Get-ChildItem

이 차이를 알면, 다음처럼 ‘7일 이내에 갱신된 파일만, 갱신 일시가 최신인 순으로 표시한다’ 같은 처리를 자연스럽게 작성할 수 있습니다.

Get-ChildItem -File |
  Where-Object { $_.LastWriteTime -gt (Get-Date).AddDays(-7) } |
  Sort-Object LastWriteTime -Descending |
  Select-Object Name, Length, LastWriteTime

3. Windows PowerShell 5.1과 PowerShell 7.x

Windows에는 예전부터 들어 있는 Windows PowerShell 5.1과, 새로운 계통의 PowerShell 7.x가 있습니다.

실무에서는 다음처럼 생각하면 이해하기 쉽습니다.

종류 실행 파일 주된 용도
Windows PowerShell 5.1 powershell.exe 오래된 Windows 전용 모듈, 기존 스크립트, 사내 자산 유지
PowerShell 7.x pwsh.exe 신규 스크립트, 크로스 플랫폼, 지속적인 기능 개선

새로 배우는 경우라면 PowerShell 7.x를 기준으로 삼아도 문제없습니다. 다만 사내의 오래된 관리 스크립트나 Windows 고유 모듈은 Windows PowerShell 5.1을 전제로 만들어진 경우가 있습니다.

그래서 실무에서는 먼저 버전을 확인합니다.

$PSVersionTable

특정 버전 이상에서만 실행하고 싶은 스크립트에는 맨 앞에 조건을 적어 두면 사고를 줄일 수 있습니다.

#Requires -Version 7.0

이 글의 샘플은 어느 쪽에서 동작하는가

이후 샘플은 별도 안내가 없으면 Windows PowerShell 5.1과 PowerShell 7.x 양쪽에서 그대로 동작하는 범위로 작성했습니다. 다만 같은 명령이라도 다음 점에서는 결과가 달라지므로, 옮겨 적기 전에 확인하십시오.

차이가 나는 곳 Windows PowerShell 5.1 PowerShell 7.x
Get-Process의 기본 표시 열 Handles, NPM(K), PM(K), WS(K), CPU(s), Id, SI, ProcessName NPM(K), PM(M), WS(M), CPU(s), Id, SI, ProcessName(메모리가 MB 단위)
-Encoding UTF8의 의미 UTF-8(BOM 있음) UTF-8(BOM 없음). BOM을 붙이려면 utf8BOM을 지정한다
-Encoding을 생략했을 때 명령마다 다름(Out-File은 UTF-16LE, Set-Content는 ANSI, Export-Csv는 ASCII) 모두 utf8NoBOM

이 글의 샘플에서는 의도하지 않은 문자 코드를 피하려고, 쓰기 계열 명령에 -Encoding UTF8을 명시했습니다. BOM 유무와 줄바꿈 코드의 기본은 Windows의 문자 인코딩과 줄바꿈 - 문자 깨짐과 CRLF/LF의 기본에서 다룹니다.

4. cmdlet 이름은 Verb-Noun

PowerShell의 표준 명령은 기본적으로 동사-명사 형태입니다.

Get-Process
Get-Service
Get-ChildItem
Copy-Item
Remove-Item
Export-Csv

이 형태를 익히면 모르는 명령도 찾기 쉬워집니다.

동사 의미
Get 가져온다 Get-Process
Set 설정한다 Set-Location
New 만든다 New-Item
Copy 복사한다 Copy-Item
Move 이동한다 Move-Item
Remove 삭제한다 Remove-Item
Start 시작한다 Start-Service
Stop 정지한다 Stop-Process
Import 읽어 들인다 Import-Csv
Export 내보낸다 Export-Csv

dir, ls, cat, cd 같은 짧은 이름도 쓸 수 있지만, 대부분은 별칭입니다.

Get-Command dir

스크립트로 남길 때는 별칭이 아니라 정식 이름을 쓰는 편이 안전합니다.

# 읽을 수는 있지만, 스크립트에서는 피하고 싶다
ls *.log

# 의도가 분명하다
Get-ChildItem -Filter *.log

5. 먼저 익힐 세 가지 조사 명령

PowerShell은 명령 수가 많기 때문에, 외우기보다 ‘조사하는 방법’을 익히는 편이 실무적입니다.

1. Get-Command ── 쓸 수 있는 명령을 찾는다

Get-Command는 설치된 명령, 함수, 별칭, 애플리케이션을 찾기 위한 명령입니다.

# Process라는 명사를 가진 명령을 찾는다
Get-Command -Noun Process

# Service 관련 명령을 찾는다
Get-Command *Service*

# CSV 관련 명령을 찾는다
Get-Command *Csv*

명령 이름이 어렴풋할 때는 와일드카드를 씁니다.

Get-Command *Item*
Get-Command *Content*
Get-Command *Json*

2. Get-Help ── 사용법을 본다

Get-Help는 명령의 설명, 매개변수, 예를 확인하기 위한 명령입니다.

Get-Help Get-ChildItem
Get-Help Get-ChildItem -Examples
Get-Help Get-ChildItem -Full
Get-Help Get-ChildItem -Online

처음에는 -Examples가 편리합니다.

Get-Help Where-Object -Examples

도움말이 오래되었거나 부족한 경우에는, 관리자 권한의 PowerShell에서 도움말을 갱신합니다.

Update-Help

환경에 따라 인터넷 연결이나 권한이 필요합니다. 회사 PC에서는 프록시나 관리 정책 때문에 실패하는 경우도 있습니다.

3. Get-Member ── 객체의 내용을 본다

PowerShell의 출력은 많은 경우 ‘문자열’이 아니라 ‘객체’입니다. Get-Member를 쓰면 그 객체가 어떤 속성이나 메서드를 가지고 있는지 확인할 수 있습니다.

Get-Process | Get-Member
Get-Service | Get-Member
Get-ChildItem | Get-Member

예를 들어 서비스에는 StatusName 같은 속성이 있습니다.

Get-Service | Select-Object Name, Status

파일에는 Name, Length, LastWriteTime 등이 있습니다.

Get-ChildItem -File | Select-Object Name, Length, LastWriteTime

‘무엇으로 좁혀야 할지 모르겠다’면, 먼저 Get-Member로 속성 이름을 확인합니다.

6. 파이프라인의 기본

PowerShell의 |는 왼쪽 명령의 결과를 오른쪽 명령에 넘깁니다.

Get-Process | Sort-Object CPU -Descending | Select-Object -First 10

이 예는 다음 처리를 왼쪽에서 오른쪽으로 합니다.

프로세스 목록을 가져온다
   CPU 사용량으로 내림차순 정렬한다
   상위 10건만 표시한다

출력은 어떻게 표시되는가

Get-Process의 결과는 기본적으로 다음 열을 가진 표로 표시됩니다. 값은 환경마다 다르므로, 확인할 것은 ‘열의 순서’입니다.

NPM(K)    PM(M)      WS(M)     CPU(s)      Id  SI ProcessName
------    -----      -----     ------      --  -- -----------
의미
NPM(K) 비페이징 풀의 메모리 사용량(KB)
PM(M) 페이징 가능한 메모리 사용량(MB)
WS(M) 작업 집합(최근에 참조된 메모리 페이지)의 크기(MB)
CPU(s) 모든 프로세서에서 사용한 프로세서 시간(초)
Id 프로세스 ID
SI 세션 ID
ProcessName 프로세스 이름

이것은 PowerShell 7.x의 기본 표시입니다. Windows PowerShell 5.1에서는 맨 앞에 Handles(열려 있는 핸들 수)가 붙고, 메모리 열이 PM(K)·WS(K)로 KB 단위가 됩니다. 열 이름이 달라도 Sort-ObjectWhere-Object에 지정하는 속성 이름(CPU, WorkingSet, Id, Name 등)은 같습니다. 표시용 열 이름과 속성 이름은 별개이므로, 좁히는 조건이 적용되지 않으면 Get-Process | Get-Member로 실제 속성 이름을 확인합니다.

자주 쓰는 조합은 다음과 같습니다.

명령 역할
Where-Object 조건으로 좁힌다 정지 중인 서비스만 가져온다
Sort-Object 정렬한다 갱신 일시가 최신인 순으로 정렬한다
Select-Object 열이나 건수를 고른다 이름과 크기만 표시한다
ForEach-Object 각 요소를 처리한다 파일마다 처리한다
Export-Csv CSV로 내보낸다 조사 결과를 저장한다

Where-Object ── 조건으로 좁힌다

# 정지 중인 서비스만 표시
Get-Service | Where-Object { $_.Status -eq "Stopped" }

# 100MB를 넘는 파일만 표시
Get-ChildItem -File |
  Where-Object { $_.Length -gt 100MB }

# 이름에 backup이 포함된 파일만 표시
Get-ChildItem -File |
  Where-Object { $_.Name -like "*backup*" }

$_는 파이프라인으로 흘러온 현재 객체를 나타냅니다.

Where-Object { $_.Length -gt 100MB }

이것은 ‘지금 보고 있는 파일의 Length가 100MB보다 큰가’를 판정합니다.

PowerShell 3.0 이후에는 간략 구문도 쓸 수 있습니다.

Get-Service | Where-Object Status -EQ "Stopped"

다만 초보 단계에서는 scriptblock 구문이 의미를 따라가기 쉽습니다.

Get-Service | Where-Object { $_.Status -eq "Stopped" }

Sort-Object ── 정렬한다

# 메모리 사용량이 큰 순
Get-Process |
  Sort-Object WorkingSet -Descending |
  Select-Object -First 10 Name, Id, WorkingSet

# 갱신 일시가 최신인 순
Get-ChildItem -File |
  Sort-Object LastWriteTime -Descending |
  Select-Object -First 20 Name, LastWriteTime

Select-Object ── 필요한 열만 고른다

Get-Process |
  Select-Object Name, Id, CPU, WorkingSet

건수를 줄일 때도 씁니다.

Get-Process | Select-Object -First 5
Get-Process | Select-Object -Last 5

CSV로 내보내기 전에는 Select-Object로 필요한 열만 정리해 두면 다루기 쉬워집니다.

Get-Service |
  Select-Object Name, DisplayName, Status, StartType |
  Export-Csv .\services.csv -NoTypeInformation -Encoding UTF8

7. Format 계열 명령은 마지막에 쓴다

PowerShell에는 표시를 다듬기 위한 명령이 있습니다.

Format-Table
Format-List
Format-Wide

이들은 ‘화면 표시용’입니다. CSV로 내보내기 전이나 후속 처리에 넘기기 전에 쓰면, 원래 속성이 아니라 표시용 객체가 되어 버립니다.

# 피할 것: CSV에 표시용 정보가 섞인다
Get-Process |
  Format-Table Name, CPU |
  Export-Csv .\process.csv -NoTypeInformation

# 올바른 예: 먼저 속성을 고른 뒤 CSV로 내보낸다
Get-Process |
  Select-Object Name, CPU |
  Export-Csv .\process.csv -NoTypeInformation -Encoding UTF8

Format-*는 ‘마지막에 화면에 보여 주기 위해’라고 기억하면 사고가 줄어듭니다.

8. 파일·폴더 조작의 기본

PowerShell에서 가장 자주 쓰는 것은 파일이나 폴더 조작입니다.

하고 싶은 일 명령
현재 위치를 본다 Get-Location
위치를 이동한다 Set-Location
목록을 본다 Get-ChildItem
파일·폴더를 만든다 New-Item
복사한다 Copy-Item
이동한다 Move-Item
이름을 바꾼다 Rename-Item
삭제한다 Remove-Item
존재 여부를 확인한다 Test-Path

현재 위치를 확인한다

Get-Location

이동합니다.

Set-Location C:\Work

경로에 공백이 있으면 따옴표로 감쌉니다.

Set-Location "C:\Work Files\Reports"

파일 목록을 본다

Get-ChildItem
Get-ChildItem -File
Get-ChildItem -Directory
Get-ChildItem -Recurse

실행하면 대상 폴더의 제목 아래에 다음 열의 표가 표시됩니다.

   Directory: C:\Work

Mode                LastWriteTime         Length Name
----                -------------         ------ ----

Mode는 속성을 나타내는 문자로, d가 디렉터리, a가 아카이브 속성, r이 읽기 전용, h가 숨김, s가 시스템, l이 링크입니다. 디렉터리 행에서는 Length(크기)가 빈칸이 됩니다. 여기에 나온 LastWriteTime이나 Length가 그대로 Where-ObjectSort-Object에서 쓸 수 있는 속성 이름입니다.

확장자로 좁힐 때는 -Filter가 편리합니다.

Get-ChildItem -Filter *.log

하위 폴더까지 포함해 검색할 때는 -Recurse를 씁니다.

Get-ChildItem C:\Logs -Filter *.log -Recurse

파일이 많은 곳에서는 처음부터 -Recurse하지 말고, 대상 폴더를 한정한 뒤에 실행합니다.

만든다

# 폴더 만들기
New-Item -ItemType Directory -Path .\archive

# 빈 파일 만들기
New-Item -ItemType File -Path .\memo.txt

이미 있을 수 있으면 먼저 확인합니다.

if (-not (Test-Path .\archive)) {
  New-Item -ItemType Directory -Path .\archive
}

복사한다

Copy-Item .\report.xlsx .\backup\report.xlsx

폴더째 복사할 때는 -Recurse를 씁니다.

Copy-Item .\data .\backup\data -Recurse

덮어쓰기가 포함되면 미리 영향을 확인합니다.

Copy-Item .\data .\backup\data -Recurse -WhatIf

이동한다

Move-Item .\old.log .\archive\old.log

여러 파일을 조건으로 이동할 때는 먼저 -WhatIf를 붙입니다.

Get-ChildItem .\logs -Filter *.log |
  Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } |
  Move-Item -Destination .\archive -WhatIf

문제가 없으면 -WhatIf를 뗍니다.

Get-ChildItem .\logs -Filter *.log |
  Where-Object { $_.LastWriteTime -lt (Get-Date).AddDays(-30) } |
  Move-Item -Destination .\archive

삭제한다

삭제는 특히 신중하게 다룹니다.

Remove-Item .\old.log -WhatIf

대상이 맞는지 확인한 뒤에 실행합니다.

Remove-Item .\old.log

와일드카드와 -Recurse의 조합은 강력합니다. 처음부터 운영 폴더에서 실행하지 말고, 목록 표시로 확인합니다.

# 먼저 대상을 확인
Get-ChildItem C:\Logs -Filter *.tmp -Recurse |
  Select-Object FullName, Length, LastWriteTime

# 다음으로 삭제 예정을 확인
Get-ChildItem C:\Logs -Filter *.tmp -Recurse |
  Remove-Item -WhatIf

# 마지막으로 실행
Get-ChildItem C:\Logs -Filter *.tmp -Recurse |
  Remove-Item

9. 텍스트 파일을 읽고·찾고·쓴다

로그 조사에서는 텍스트 파일 조작을 자주 씁니다.

파일을 읽는다

Get-Content .\app.log

끝부분만 볼 때는 -Tail을 씁니다.

Get-Content .\app.log -Tail 50

이어 붙는 로그를 볼 때는 -Wait를 씁니다.

Get-Content .\app.log -Tail 20 -Wait

문자열을 검색한다

Select-String -Path .\app.log -Pattern "ERROR"

여러 파일을 대상으로 할 수 있습니다.

Select-String -Path .\logs\*.log -Pattern "ERROR", "WARN"

검색 결과에서 파일 이름·행 번호·내용을 꺼내 CSV로 만들 수도 있습니다.

Select-String -Path .\logs\*.log -Pattern "ERROR" |
  Select-Object Path, LineNumber, Line |
  Export-Csv .\error-lines.csv -NoTypeInformation -Encoding UTF8

파일에 쓴다

"hello" | Set-Content .\memo.txt -Encoding UTF8
"next line" | Add-Content .\memo.txt -Encoding UTF8

명령 결과를 저장할 때는 Out-File보다 Export-CsvConvertTo-Json이 나중에 다루기 쉬운 경우가 있습니다.

Get-Process |
  Select-Object Name, Id, CPU |
  Export-Csv .\process.csv -NoTypeInformation -Encoding UTF8

10. CSV를 다루는 기본

업무 자동화에서는 CSV가 매우 자주 나옵니다. PowerShell에서는 CSV를 문자열이 아니라 ‘열을 가진 객체’로 다룰 수 있습니다.

CSV를 읽어 들인다

예를 들어 다음과 같은 users.csv가 있다고 합시다.

Name,Department,Enabled
Suzuki,Sales,true
Tanaka,Accounting,false
Sato,Sales,true

읽어 들입니다.

$users = Import-Csv .\users.csv
$users

열 이름이 속성이 됩니다.

$users | Select-Object Name, Department

조건으로 좁힌다

$users |
  Where-Object { $_.Department -eq "Sales" }

CSV 값은 문자열로 들어오는 경우가 많으므로, true / false나 숫자를 다룰 때는 주의합니다.

$users |
  Where-Object { $_.Enabled -eq "true" }

CSV로 내보낸다

$users |
  Where-Object { $_.Department -eq "Sales" } |
  Export-Csv .\sales-users.csv -NoTypeInformation -Encoding UTF8

Export-Csv 앞에 Format-Table을 넣지 않는 것이 철칙입니다.

# 피할 것
$users | Format-Table | Export-Csv .\out.csv -NoTypeInformation

# 올바른 예
$users | Select-Object Name, Department, Enabled | Export-Csv .\out.csv -NoTypeInformation -Encoding UTF8

11. JSON을 다루는 기본

설정 파일이나 Web API에서는 JSON도 자주 씁니다.

$data = Get-Content .\settings.json -Raw | ConvertFrom-Json
$data

객체를 JSON으로 변환합니다.

[pscustomobject]@{
  Name = "BatchJob"
  Enabled = $true
  Retry = 3
} | ConvertTo-Json

깊은 계층이 있으면 -Depth를 지정합니다.

$config | ConvertTo-Json -Depth 10 | Set-Content .\settings.json -Encoding UTF8

12. 프로세스와 서비스의 기본

프로세스를 본다

Get-Process

메모리 사용량이 큰 프로세스를 확인합니다.

Get-Process |
  Sort-Object WorkingSet -Descending |
  Select-Object -First 10 Name, Id, WorkingSet

이름으로 좁힙니다.

Get-Process -Name notepad

정지는 신중하게 합니다.

Stop-Process -Name notepad -WhatIf

문제가 없으면 실행합니다.

Stop-Process -Name notepad

서비스를 본다

Get-Service

정지 중인 서비스만 보는 경우입니다.

Get-Service |
  Where-Object { $_.Status -eq "Stopped" }

이름으로 좁힙니다.

Get-Service -Name "Spooler"

다시 시작할 때도 대상 확인을 먼저 합니다.

Get-Service -Name "Spooler"
Restart-Service -Name "Spooler" -WhatIf

서비스 조작은 업무 영향이 나오기 쉬우므로, 운영 환경에서는 절차서, 유지 보수 시간, 복구 방법을 먼저 확인합니다.

13. 이벤트 로그의 기본

Windows 조사에서는 이벤트 로그가 빠질 수 없습니다.

최근 오류를 확인합니다.

Get-WinEvent -LogName System -MaxEvents 100 |
  Where-Object { $_.LevelDisplayName -eq "Error" } |
  Select-Object TimeCreated, ProviderName, Id, Message

출력은 어떻게 표시되는가

Get-WinEvent의 결과는 기본적으로 로그 공급자(ProviderName)마다 제목이 붙고, 그 아래에 다음 열의 표가 이어집니다.

   ProviderName: PowerShell

TimeCreated              Id LevelDisplayName  Message
-----------              -- ----------------  -------

위 예처럼 Select-Object로 열을 고르면 이 공급자 제목은 붙지 않고, 고른 열만의 표가 됩니다. Message는 길어서 화면에서는 중간에 잘립니다. 한 건을 끝까지 읽으려면 대상을 1건으로 좁힌 뒤 Format-List로 표시합니다.

Get-WinEvent -LogName System -MaxEvents 1 | Format-List *

LevelDisplayName에는 Critical, Error, Warning, Information, Verbose가 들어갑니다. 건수가 많은 로그에서는 Where-Object로 좁히기보다 -FilterHashtable에 조건을 넘기는 편이 빨라집니다(Level = 3이 경고, Level = 4가 정보입니다).

애플리케이션 로그의 최근 50건을 봅니다.

Get-WinEvent -LogName Application -MaxEvents 50 |
  Select-Object TimeCreated, ProviderName, Id, LevelDisplayName, Message

기간으로 좁히는 경우입니다.

$start = (Get-Date).AddHours(-24)

Get-WinEvent -FilterHashtable @{
  LogName = "System"
  StartTime = $start
} |
  Select-Object TimeCreated, ProviderName, Id, LevelDisplayName, Message

조사 결과는 CSV로 남겨 두면 나중에 공유하기 쉬워집니다.

Get-WinEvent -LogName System -MaxEvents 500 |
  Where-Object { $_.LevelDisplayName -in @("Error", "Warning") } |
  Select-Object TimeCreated, ProviderName, Id, LevelDisplayName, Message |
  Export-Csv .\system-events.csv -NoTypeInformation -Encoding UTF8

14. 변수·배열·해시 테이블

짧은 명령만으로도 작업할 수 있지만, 조금 길어지면 변수가 편리합니다.

변수

$path = "C:\Logs"
Get-ChildItem $path

PowerShell의 변수는 $로 시작합니다.

$today = Get-Date
$limit = (Get-Date).AddDays(-30)

배열

$extensions = @("*.log", "*.txt", "*.csv")
foreach ($ext in $extensions) {
  Get-ChildItem C:\Work -Filter $ext
}

해시 테이블

해시 테이블은 키와 값의 조합입니다.

$params = @{
  Path = "C:\Logs"
  Filter = "*.log"
  Recurse = $true
}

Get-ChildItem @params

@params 같은 쓰는 법을 ‘스플래팅’이라고 합니다. 매개변수가 늘었을 때 읽기 쉬워집니다.

15. PSCustomObject로 결과를 다듬는다

조사 결과를 표 형태로 모으고 싶을 때는 [pscustomobject]가 편리합니다.

[pscustomobject]@{
  ComputerName = $env:COMPUTERNAME
  UserName     = $env:USERNAME
  CheckedAt    = Get-Date
}

여러 결과를 만들어 CSV로 낼 수 있습니다.

Get-ChildItem C:\Logs -Filter *.log |
  ForEach-Object {
    [pscustomobject]@{
      Name          = $_.Name
      FullName      = $_.FullName
      SizeMB        = [math]::Round($_.Length / 1MB, 2)
      LastWriteTime = $_.LastWriteTime
    }
  } |
  Export-Csv .\log-files.csv -NoTypeInformation -Encoding UTF8

16. 스크립트 파일 .ps1의 기본

자주 쓰는 처리는 .ps1 파일로 만듭니다.

예로 오래된 로그를 목록화하는 스크립트를 만듭니다.

# Find-OldLogs.ps1
param(
  [string]$Path = "C:\Logs",
  [int]$Days = 30,
  [string]$OutputPath = ".\old-logs.csv"
)

$limit = (Get-Date).AddDays(-$Days)

Get-ChildItem -Path $Path -Filter *.log -File -Recurse |
  Where-Object { $_.LastWriteTime -lt $limit } |
  Select-Object FullName, Length, LastWriteTime |
  Export-Csv -Path $OutputPath -NoTypeInformation -Encoding UTF8

Write-Host "Exported: $OutputPath"

실행 예입니다.

.\Find-OldLogs.ps1 -Path C:\Logs -Days 60 -OutputPath .\old-logs.csv

인수를 param()으로 받으면 나중에 조건을 바꾸기 쉬워집니다.

17. 실행 정책의 기본

.ps1을 실행하려다 다음과 같은 오류가 나는 경우가 있습니다.

このシステムではスクリプトの実行が無効になっているため...

그럴 때는 현재 실행 정책을 확인합니다.

Get-ExecutionPolicy
Get-ExecutionPolicy -List

개인 개발 단말에서 로컬에서 만든 스크립트만 실행하고 싶다면, 현재 사용자 범위에서 RemoteSigned로 두는 경우가 많습니다.

Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

다만 회사 PC에서는 그룹 정책으로 제어되는 경우가 있습니다. 무리하게 우회하지 말고, 관리자나 운영 규칙을 확인합니다.

실행 정책은 PowerShell의 스크립트 실행 조건을 제어하는 안전 기능입니다. 다만 완전한 보안 경계는 아닙니다. 조직에서는 서명, AppLocker, Microsoft Defender, 권한 관리, 로그 감사와 함께 생각해야 합니다.

18. 오류 처리의 기본

PowerShell은 오류가 나도 처리를 계속하는 경우가 있습니다.

중요한 처리에서는 -ErrorAction Stoptry/catch를 씁니다.

try {
  Copy-Item .\source.txt .\backup\source.txt -ErrorAction Stop
  Write-Host "Copy succeeded"
}
catch {
  Write-Error "Copy failed: $($_.Exception.Message)"
}

여러 파일을 처리할 때도 실패를 기록하면 나중에 추적할 수 있습니다.

$results = foreach ($file in Get-ChildItem .\input -File) {
  try {
    Copy-Item $file.FullName .\backup -ErrorAction Stop

    [pscustomobject]@{
      FileName = $file.Name
      Status   = "OK"
      Message  = ""
    }
  }
  catch {
    [pscustomobject]@{
      FileName = $file.Name
      Status   = "NG"
      Message  = $_.Exception.Message
    }
  }
}

$results | Export-Csv .\copy-result.csv -NoTypeInformation -Encoding UTF8

19. 변경 계열 명령을 안전하게 실행하는 순서

PowerShell에서는 삭제·이동·정지 같은 변경 계열 명령을 안전하게 다루는 절차가 중요합니다.

기본은 다음 순서입니다.

1. Get 계열로 대상을 본다
2. Where-Object 좁힌다
3. Select-Object 대상 목록을 확인한다
4. Export-Csv 기록한다
5. -WhatIf 변경 예정을 확인한다
6. 실제로 실행한다

예로 30일보다 오래된 .tmp 파일을 삭제하는 경우입니다.

Step 1: 대상을 본다

Get-ChildItem C:\Temp -Filter *.tmp -File -Recurse

Step 2: 조건으로 좁힌다

$limit = (Get-Date).AddDays(-30)

Get-ChildItem C:\Temp -Filter *.tmp -File -Recurse |
  Where-Object { $_.LastWriteTime -lt $limit }

Step 3: 필요한 열만 표시한다

$targets = Get-ChildItem C:\Temp -Filter *.tmp -File -Recurse |
  Where-Object { $_.LastWriteTime -lt $limit }

$targets |
  Select-Object FullName, Length, LastWriteTime

Step 4: 기록을 남긴다

$targets |
  Select-Object FullName, Length, LastWriteTime |
  Export-Csv .\delete-targets.csv -NoTypeInformation -Encoding UTF8

Step 5: WhatIf로 확인한다

$targets | Remove-Item -WhatIf

Step 6: 실행한다

$targets | Remove-Item

이 순서로 하면 ‘무엇을 지웠는지 모르겠다’는 사고를 줄일 수 있습니다.

20. 자주 쓰는 기본 명령 목록

위치·파일

목적 명령 예
현재 폴더를 표시 Get-Location
폴더 이동 Set-Location C:\Work
목록 표시 Get-ChildItem
파일만 표시 Get-ChildItem -File
폴더만 표시 Get-ChildItem -Directory
하위 폴더도 검색 Get-ChildItem -Recurse
존재 확인 Test-Path .\file.txt
폴더 만들기 New-Item -ItemType Directory .\backup
복사 Copy-Item .\a.txt .\backup\a.txt
이동 Move-Item .\a.txt .\archive\a.txt
삭제 예정 확인 Remove-Item .\a.txt -WhatIf

객체 가공

목적 명령 예
조건으로 좁힌다 Where-Object { $_.Status -eq "Running" }
정렬한다 Sort-Object LastWriteTime -Descending
열을 고른다 Select-Object Name, LastWriteTime
상위만 표시 Select-Object -First 10
각 요소를 처리 ForEach-Object { $_.Name }
내용을 조사 Get-Member

입출력

목적 명령 예
텍스트를 읽는다 Get-Content .\app.log
끝부분을 본다 Get-Content .\app.log -Tail 50
문자열 검색 Select-String -Path .\app.log -Pattern "ERROR"
CSV 읽어 들이기 Import-Csv .\users.csv
CSV 내보내기 Export-Csv .\out.csv -NoTypeInformation -Encoding UTF8
JSON 읽어 들이기 Get-Content .\a.json -Raw | ConvertFrom-Json
JSON 내보내기 $obj | ConvertTo-Json -Depth 10

Windows 조사

목적 명령 예
프로세스 목록 Get-Process
서비스 목록 Get-Service
이벤트 로그 Get-WinEvent -LogName System -MaxEvents 100
환경 변수 Get-ChildItem Env:
PowerShell 버전 $PSVersionTable
실행 정책 Get-ExecutionPolicy -List

21. 실무 샘플: 로그를 조사해 보고서를 만든다

다음 같은 요건을 생각합니다.

C:\App\Logs 아래의 .log 파일에서, 최근 7일 이내에 갱신되고 ERROR를 포함한 행을 찾아 CSV로 모으고 싶다.

처음부터 완성형을 쓰지 말고 단계적으로 만듭니다.

Step 1: 로그 파일을 찾는다

Get-ChildItem C:\App\Logs -Filter *.log -File -Recurse

Step 2: 최근 7일 이내로 좁힌다

$since = (Get-Date).AddDays(-7)

Get-ChildItem C:\App\Logs -Filter *.log -File -Recurse |
  Where-Object { $_.LastWriteTime -ge $since }

Step 3: ERROR를 검색한다

$since = (Get-Date).AddDays(-7)

Get-ChildItem C:\App\Logs -Filter *.log -File -Recurse |
  Where-Object { $_.LastWriteTime -ge $since } |
  Select-String -Pattern "ERROR"

Step 4: CSV로 내보낸다

$since = (Get-Date).AddDays(-7)

Get-ChildItem C:\App\Logs -Filter *.log -File -Recurse |
  Where-Object { $_.LastWriteTime -ge $since } |
  Select-String -Pattern "ERROR" |
  Select-Object Path, LineNumber, Line |
  Export-Csv .\error-report.csv -NoTypeInformation -Encoding UTF8

Step 5: 스크립트화한다

# Export-ErrorReport.ps1
param(
  [string]$LogPath = "C:\App\Logs",
  [int]$Days = 7,
  [string]$Pattern = "ERROR",
  [string]$OutputPath = ".\error-report.csv"
)

$since = (Get-Date).AddDays(-$Days)

Get-ChildItem $LogPath -Filter *.log -File -Recurse |
  Where-Object { $_.LastWriteTime -ge $since } |
  Select-String -Pattern $Pattern |
  Select-Object Path, LineNumber, Line |
  Export-Csv $OutputPath -NoTypeInformation -Encoding UTF8

Write-Host "Exported: $OutputPath"

실행 예입니다.

.\Export-ErrorReport.ps1 -LogPath C:\App\Logs -Days 14 -Pattern "ERROR|FATAL" -OutputPath .\errors.csv

22. 실무 샘플: 오래된 파일을 아카이브한다

다음은 삭제가 아니라 이동으로 하는 예입니다.

C:\Work\Reports 중에서, 90일 이상 갱신되지 않은 .xlsx를 C:\Work\Archive로 이동한다.

대상 확인

$source = "C:\Work\Reports"
$dest = "C:\Work\Archive"
$limit = (Get-Date).AddDays(-90)

$targets = Get-ChildItem $source -Filter *.xlsx -File |
  Where-Object { $_.LastWriteTime -lt $limit }

$targets | Select-Object FullName, Length, LastWriteTime

아카이브 폴더 만들기

if (-not (Test-Path $dest)) {
  New-Item -ItemType Directory -Path $dest
}

기록 출력

$targets |
  Select-Object FullName, Length, LastWriteTime |
  Export-Csv .\archive-targets.csv -NoTypeInformation -Encoding UTF8

WhatIf 확인

$targets | Move-Item -Destination $dest -WhatIf

실행

$targets | Move-Item -Destination $dest

파일 이름 중복이 있을 수 있으면 이대로는 실패합니다. 실무에서는 연월 폴더를 나누기, 이동처 파일 이름에 일시를 붙이기, 기존 파일이 있으면 건너뛰기 같은 규칙을 먼저 정합니다.

23. 자주 하는 실수

증상 원인 대처
명령이 길어서 외울 수 없다 외우려고 한다 Get-Command, Get-Help -Examples로 찾는다
$_가 이해되지 않는다 파이프라인 현재 값의 이해 부족 Where-Object { $_.Name -like "*log*" } 형태로 익힌다
CSV 내용이 이상해진다 Format-Table 뒤에 Export-Csv하고 있다 Select-Object 뒤에 Export-Csv한다
경로에 공백이 있으면 실패한다 따옴표가 없다 "C:\Work Files\a.txt"처럼 감싼다
스크립트를 실행할 수 없다 실행 정책 Get-ExecutionPolicy -List로 확인한다
삭제 대상이 너무 많다 조건이 너무 넓다 먼저 Select-Object FullName-WhatIf로 확인한다
속성 이름을 모른다 객체 구조를 보지 않았다 Get-Member를 쓴다
글자가 깨진다 인코딩 전제가 다르다 입출력의 -Encoding과 사용하는 앱을 확인한다

24. 명령을 익히는 순서

처음부터 모든 명령을 외울 필요는 없습니다.

다음 순서를 권합니다.

제1단계: 본다·찾는다

Get-Command
Get-Help
Get-Member
Get-Location
Set-Location
Get-ChildItem
Get-Content
Select-String

제2단계: 좁힌다·다듬는다

Where-Object
Sort-Object
Select-Object
ForEach-Object
Format-Table
Format-List

제3단계: 입출력한다

Import-Csv
Export-Csv
ConvertFrom-Json
ConvertTo-Json
Set-Content
Add-Content
Out-File

제4단계: 변경한다

New-Item
Copy-Item
Move-Item
Rename-Item
Remove-Item
Start-Service
Stop-Service
Restart-Service
Stop-Process

변경 계열은 반드시 확인 계열 명령과 세트로 익힙니다.

# 본다
Get-ChildItem .\logs -Filter *.tmp

# 삭제 예정을 본다
Get-ChildItem .\logs -Filter *.tmp | Remove-Item -WhatIf

# 실행
Get-ChildItem .\logs -Filter *.tmp | Remove-Item

25. 현장 운영 체크리스트

업무에서 PowerShell을 쓸 때는 다음 관점을 확인해 두면 안전합니다.

  • 실행하는 PowerShell이 powershell.exe인지 pwsh.exe인지 확인했다
  • $PSVersionTable로 버전을 확인했다
  • 변경 전에 Get-* 계열로 대상을 표시했다
  • Where-Object의 조건을 화면에서 확인했다
  • 삭제·이동·정지 전에 -WhatIf를 썼다
  • 실행 전 대상 목록을 CSV로 저장했다
  • 운영 환경에서는 백업 또는 복구 절차를 확인했다
  • 스크립트의 실행 정책과 사내 규칙을 확인했다
  • 오류 시 로그 출력을 준비했다
  • 공유하는 스크립트에서는 별칭이 아니라 정식 명령 이름을 썼다

26. 정리

PowerShell의 기본은 명령을 대량으로 외우는 것이 아닙니다. 실무에서 효과가 나는 것은 다음 형을 익히는 것입니다.

Get-Command 찾는다
Get-Help 사용법을 본다
Get-Member 속성을 확인한다
Get-*  대상을 본다
Where-Object 좁힌다
Select-Object 열을 다듬는다
Export-Csv 기록을 남긴다
-WhatIf 변경 예정을 확인한다
마지막으로 실행한다

특히 PowerShell은 파일 삭제, 서비스 정지, 프로세스 종료, 레지스트리 조작까지 할 수 있는 강력한 환경입니다. 그래서 가장 먼저 익혀야 할 것은 ‘위험한 명령’이 아니라 ‘대상을 안전하게 확인하는 절차’입니다.

본다  좁힌다  기록한다  모의 실행한다  실행한다

이 순서를 지키면 PowerShell은 단순한 검은 화면이 아니라, Windows 업무를 정리·조사·자동화하기 위한 실용적인 도구가 됩니다.

참고 링크

관련 글

이 주제와 연결되는 서비스

Windows 앱 개발

업무용 앱, 장비 연동, 통신 도구 등 Windows 소프트웨어 개발을 지원합니다.

서비스 보기 / 문의하기

기존 자산 활용·마이그레이션 지원

오래된 배치, VBScript, VBA, PowerShell, COM / ActiveX를 포함한 기존 자산의 현황 파악, 수정, 마이그레이션을 지원합니다.

문의하기

같은 태그를 공유하는 최신 기사입니다. 더 가까운 주제로 지식을 넓힐 수 있습니다.

이 기사와 가까운 토픽 페이지입니다. 기사를 출발점 삼아 관련 서비스와 다른 기사로 이어집니다.

이 기사는 다음 서비스 페이지로 이어집니다. 가까운 입구부터 확인해 주세요.

자주 묻는 질문

이 기사 주제에 대해 상담 시 자주 나오는 질문을 모았습니다.

PowerShell 학습은 무엇부터 시작하면 좋을까요?
명령을 외우는 것이 아니라, 조사하는 방법을 익히는 것입니다. 핵심은 세 가지입니다. Get-Command와 Get-Help로 명령을 조사할 수 있을 것, 파이프라인(|)으로 결과를 다음 명령에 넘길 수 있을 것, 삭제·정지·변경 전에 -WhatIf나 -Confirm으로 영향을 확인할 것입니다. 객체의 속성 이름을 모를 때는 Get-Member로 내용을 확인합니다. 기본 흐름은 '찾는다 → 본다 → 좁힌다 → 정렬한다 → 출력한다 → 필요하면 변경한다'입니다.
PowerShell과 명령 프롬프트는 무엇이 다른가요?
가장 큰 차이는 명령 프롬프트의 출력이 문자열 중심인 데 비해, PowerShell의 출력은 객체라는 점입니다. 예를 들어 파일은 Name, Length, LastWriteTime 같은 속성을 가진 객체로 다루어지므로, Where-Object로 좁히거나 Sort-Object로 정렬하는 코드를 자연스럽게 작성할 수 있습니다. 명령 이름도 Get-ChildItem이나 Copy-Item처럼 동사-명사 형태로 통일되어 있어, 모르는 명령도 찾기 쉽습니다.
Windows PowerShell 5.1과 PowerShell 7.x 중 어느 쪽을 써야 하나요?
새로 배우는 경우라면 PowerShell 7.x(pwsh.exe)를 기준으로 삼아도 문제없습니다. 다만 사내의 오래된 관리 스크립트나 Windows 고유 모듈은 Windows PowerShell 5.1(powershell.exe)을 전제로 만들어진 경우가 있습니다. 실무에서는 먼저 $PSVersionTable로 버전을 확인하고, 특정 버전 이상에서만 실행하고 싶은 스크립트에는 맨 앞에 #Requires -Version 7.0과 같은 조건을 적어 두면 사고를 줄일 수 있습니다.
PowerShell에서 삭제나 변경을 안전하게 실행하려면 어떻게 해야 하나요?
'본다 → 좁힌다 → 기록한다 → 모의 실행한다 → 실행한다'의 순서를 지키는 것입니다. 구체적으로는 먼저 Get 계열 명령으로 대상을 표시하고, Where-Object로 좁히고, Select-Object로 대상 목록을 확인하고, Export-Csv로 CSV에 기록을 남기고, -WhatIf로 변경 예정을 확인한 다음 실제로 실행합니다. PowerShell은 조사와 변경을 같은 화면에서 실행할 수 있으므로, 읽기 전용 확인부터 시작하는 습관이 중요합니다.

저자 프로필

기사 저자의 프로필 페이지입니다.

Go Komura

합동회사 코무라소프트 대표

Windows 소프트웨어 개발, 기술 상담, 장애 조사를 중심으로 재현이 어려운 장애 조사와 기존 자산이 남아 있는 프로젝트에 강점이 있습니다.

블로그 목록으로 돌아가기