PowerShell 指令基礎 ── 該先學會的操作與安全使用方式
· 小村 豪 · PowerShell, Windows, 命令列, 自動化, 既有資產活用
1. 首先該掌握的事
PowerShell 是一個命令環境,可以用來確認 Windows 設定、整理檔案、調查記錄檔、加工 CSV、操作服務,以及自動化各種定型作業。
不過,一開始並不需要寫複雜的腳本。只要先記住下面這個流程,在實務上就已經相當夠用了。
尋找 → 查看 → 篩選 → 排序 → 輸出 → 必要時再變更
PowerShell 的基礎並不是「死記冗長的指令」。真正重要的是以下三點:
- 能用
Get-Command與Get-Help查詢指令 - 能用
|管線把結果傳遞給下一個指令 - 在刪除、停止、變更之前,先用
-WhatIf或-Confirm確認影響
PowerShell 可以在同一個畫面上執行「調查」與「變更」。方便的同時,刪除或停止也能立刻執行,因此養成從唯讀確認開始的習慣非常重要。
另外,本文中出現的程式碼,已依章節主題整理成一套範例(可在暫存資料夾中建立練習用工作區,安全地嘗試),並附上 Pester 測試,整套公開在 GitHub 上。
powershell-command-basics - komurasoft-blog-samples (GitHub)
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
4. Cmdlet 的命名是「動詞-名詞」
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
依環境不同,這可能需要網路連線與相應權限。在公司電腦上,也可能因為 Proxy 或管理原則而失敗。
3. Get-Member ── 查看物件的內容
PowerShell 的輸出,多數情況下並非「字串」,而是「物件」。使用 Get-Member,就能確認該物件擁有哪些屬性與方法。
Get-Process | Get-Member
Get-Service | Get-Member
Get-ChildItem | Get-Member
舉例來說,服務就有 Status、Name 等屬性。
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 筆
常用的組合如下。
| 指令 | 作用 | 範例 |
|---|---|---|
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
依副檔名篩選時,-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-Csv 或 ConvertTo-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
查看應用程式記錄檔中最近 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 的寫法稱為「參數展開(splatting)」。當參數變多時,可以讓程式碼更容易閱讀。
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
不過,公司電腦有時會由群組原則統一控管。不要勉強繞過限制,應先向管理員或依循運作規則確認。
執行原則是用來控管 PowerShell 腳本執行條件的安全機制。但它並不是完整的安全邊界。在組織中,需要與簽署、AppLocker、Microsoft Defender、權限管理、記錄稽核一併考量。
18. 錯誤處理的基礎
PowerShell 有時即使發生錯誤,仍會繼續執行。
在重要的處理中,要使用 -ErrorAction Stop 與 try/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 檔案為例。
步驟 1:查看對象
Get-ChildItem C:\Temp -Filter *.tmp -File -Recurse
步驟 2:依條件篩選
$limit = (Get-Date).AddDays(-30)
Get-ChildItem C:\Temp -Filter *.tmp -File -Recurse |
Where-Object { $_.LastWriteTime -lt $limit }
步驟 3:只顯示必要的欄位
$targets = Get-ChildItem C:\Temp -Filter *.tmp -File -Recurse |
Where-Object { $_.LastWriteTime -lt $limit }
$targets |
Select-Object FullName, Length, LastWriteTime
步驟 4:留下記錄
$targets |
Select-Object FullName, Length, LastWriteTime |
Export-Csv .\delete-targets.csv -NoTypeInformation -Encoding UTF8
步驟 5:用 WhatIf 確認
$targets | Remove-Item -WhatIf
步驟 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。
不要一開始就寫出完成版,逐步進行建置。
步驟 1:尋找記錄檔
Get-ChildItem C:\App\Logs -Filter *.log -File -Recurse
步驟 2:篩選最近 7 天內的檔案
$since = (Get-Date).AddDays(-7)
Get-ChildItem C:\App\Logs -Filter *.log -File -Recurse |
Where-Object { $_.LastWriteTime -ge $since }
步驟 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"
步驟 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
步驟 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.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 業務的實用工具。
參考連結
- 本文範例程式碼完整版(依主題分類的腳本・Pester 測試) - komurasoft-blog-samples (GitHub)
- PowerShell Documentation - Microsoft Learn
- 在 Windows 上安裝 PowerShell 7 - Microsoft Learn
- Windows PowerShell 5.1 與 PowerShell 7.x 的差異 - Microsoft Learn
- Get-Command - Microsoft Learn
- Get-Help - Microsoft Learn
- Get-Member - Microsoft Learn
- about_Pipelines - Microsoft Learn
- Where-Object - Microsoft Learn
- Select-Object - Microsoft Learn
- Import-Csv - Microsoft Learn
- Export-Csv - Microsoft Learn
- about_Execution_Policies - Microsoft Learn
相關文章
與此主題相關的服務
Windows 應用程式開發
支援業務應用程式、裝置整合、通訊工具等 Windows 軟體開發。
既有資產活用・移轉支援
支援盤點、改善、移轉既有資產,包含舊有的批次檔、VBScript、VBA、PowerShell、COM / ActiveX。
相關文章
共用相同標籤的最新文章。能以相近的主題延伸理解。
PowerShell 實用指令集錦 ── 累積日常工作常用的小工具
本文整理 PowerShell 日常工作中常用的實用指令,說明 Measure-Object、Group-Object、Select-String、Compare-Object、Tee-Object、Start-Transcript 等指令的使用場景與時機。
在 C#(CSharp)中執行 PowerShell 並以物件形式接收結果
本文從實務角度整理如何從 C# 啟動 PowerShell,並以 PSObject 而非字串來接收結果,涵蓋 PowerShell SDK、AddCommand、AddParameter、BaseObject、Properties 到錯誤處理。
用 Pester 整備 PowerShell 測試 ── 讓維運腳本不易損壞的實務做法
本文整理用 Pester v5 測試 PowerShell 腳本的實務步驟,涵蓋日期處理、檔案操作、刪除處理、Mock,一路到 CI 執行,協助安全地建立測試基礎。
PowerShell 腳本應用 ── 安全地自動化日誌調查、封存與報表化
本文整理用 PowerShell 腳本安全地推進日誌調查、CSV 報表、舊日誌封存、留存紀錄,一直到工作排程器定期執行的實務步驟。
為 VBScript 停用做準備:VBA・企業內部工具盤點指南
本文整理 VBScript 分階段停用前,VBA、Excel 巨集與企業內部工具的資產盤點、靜態偵測、執行紀錄蒐集、替代技術選型、測試與分階段導入的做法。
相關主題
與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。
Windows 技術主題
彙整 KomuraSoft LLC 關於 Windows 開發、故障調查與既有資產活用文章的主題中心。
與本主題相關的服務
本文連結到以下服務頁面,歡迎從最接近的入口查看。
Windows 應用程式開發
支援包含常駐處理、設備連動、運作日誌與可維護結構的 Windows 桌面應用程式。
既有資產活用 & 遷移支援
在持續活用 COM / ActiveX / OCX 資產、原生程式碼與 32 位元相依的同時,協助規劃階段性的遷移。
常見問題
整理諮詢這個主題時常見的問題。
- 學習 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 軟體開發、技術諮詢與故障調查為中心,在難以重現的故障調查與既有資產仍在運作的專案上具有優勢。