PowerShell 實用指令集錦 ── 累積日常工作常用的小工具
· 小村 豪 · PowerShell, Windows, 命令列, 自動化, 業務效率化, 既有資產活用
1. 首先要掌握的事
學會 PowerShell 的基礎之後,接下來要做的是累積「常用的小工具」,這樣在實務上會更好用。
這裡說的小工具,並不是指簡短的別名。這篇文章不會用 ls 或 cat 這類簡寫,而是使用 Get-ChildItem、Get-Content、Select-String 這類正式的指令名稱。
在此基礎上,讓下面這些工作都能用一行或幾行指令完成:
數件數
查總容量
去除重複
依種類統計
只取出需要的值
比較兩份結果
查看記錄檔前後幾行
傳送到剪貼簿
儲存執行結果的同時也顯示在畫面上
確認環境變數與網路狀態
PowerShell 不需要寫很長的腳本也能派上用場。先累積「常用的零件」,再在日常工作中逐步組合運用。
與其死記指令,更重要的是能想起「這種情況可以用這個零件」。
順帶一提,本文出現的程式碼,已依章節主題整理成一套可直接執行的範例(含 recipe 腳本與 Pester 測試),發表在 GitHub 上。
powershell-practical-command-recipes - komurasoft-blog-samples (GitHub)
2. 這次的思考方式
前提有以下 3 點:
- 指令名稱用正式全名書寫
- 以讀取類指令為中心
- 變更類指令一定要搭配對象確認或
-WhatIf一起思考
舉例來說,與其一開始就急著學刪除檔案的指令,先養成下面這個流程的習慣會更安全:
查看對象
→ 用條件篩選
→ 數清件數
→ 必要時留存為 CSV
→ 確認變更計畫
→ 執行
這篇文章的重點會放在調查、統計、比較、輸出,而不是刪除、停止這類強力操作。
3. 常用指令地圖
這次會用到的主要指令如下。
| 想做的事 | 指令 |
|---|---|
| 查件數或總計 | Measure-Object |
| 整理重複項目 | Sort-Object -Unique |
| 依種類統計 | Group-Object |
| 只輸出需要的欄位 | Select-Object |
| 逐一處理每個元素 | ForEach-Object |
| 搜尋字串 | Select-String |
| 比較兩份結果 | Compare-Object |
| 計算雜湊值 | Get-FileHash |
| 使用剪貼簿 | Set-Clipboard / Get-Clipboard |
| 同時輸出到畫面與檔案 | Tee-Object |
| 測量處理時間 | Measure-Command |
| 查看錯誤細節 | Get-Error |
| 確認網路連通性 | Test-Connection / Test-NetConnection |
不需要一次全部記起來。建議搭配目的一起記,例如「要數就用 Measure-Object」「要彙整就用 Group-Object」「要比較就用 Compare-Object」。
4. 數件數 ── Measure-Object
最先常用到的,就是數件數的操作。
計算目前資料夾內的檔案數。
Get-ChildItem . -File | Measure-Object
連子資料夾一起算的情況。
Get-ChildItem . -File -Recurse | Measure-Object
Measure-Object 結果中的 Count,就是件數。
想看檔案總容量時,統計 Length。
Get-ChildItem . -File -Recurse |
Measure-Object -Property Length -Sum
不過這樣顯示的是位元組數,人類不太好讀,所以換算成 GB 單位。
$size = Get-ChildItem . -File -Recurse |
Measure-Object -Property Length -Sum
[math]::Round($size.Sum / 1GB, 2)
這在想大致掌握整個資料夾容量時很方便。
5. 順便查最大值與平均值
Measure-Object 不只能算總和,也能算出最大值與平均值。
Get-ChildItem . -File |
Measure-Object -Property Length -Sum -Average -Maximum -Minimum
這在觀察檔案大小分佈時可以用到。
也可以加總處理程序累積使用的 CPU 時間。Get-Process 的 CPU 不是使用率,而是處理程序啟動以來累積使用的 CPU 秒數。
Get-Process |
Measure-Object -Property CPU -Sum
不過這個數值是累積值,不是瞬間的 CPU 使用率,所以會隨執行時機與處理程序的運作時間而變化。
拿來做調查用途已經足夠,但若要做嚴謹的監控,還需要專門的監控工具與記錄檔設計。
6. 只取出值 ── Select-Object -ExpandProperty
Select-Object 常用來挑選欄位。
Get-Process |
Select-Object Name, Id, CPU
這會輸出「帶有 Name、Id、CPU 這些欄位的物件」。
另一方面,只想取出值時,用 -ExpandProperty。
Get-Process -Name notepad -ErrorAction SilentlyContinue |
Select-Object -ExpandProperty Id
只想取出檔名時也一樣。
Get-ChildItem . -File |
Select-Object -ExpandProperty Name
想把指令結果傳給其他指令或文字檔時,-ExpandProperty 很方便。
例如,只儲存檔名清單。
Get-ChildItem . -File |
Select-Object -ExpandProperty Name |
Set-Content .\file-names.txt -Encoding UTF8
Select-Object Name 和 Select-Object -ExpandProperty Name 看起來相似,但結果的形式不同。
Select-Object Name
→ 帶有 Name 欄位的表格
Select-Object -ExpandProperty Name
→ Name 的值本身
想輸出成 CSV 的話,保留成欄位比較好;想傳給其他字串處理的話,只有值會比較好處理。
7. 去除重複 ── Sort-Object -Unique
想列出檔案副檔名清單的情況。
Get-ChildItem . -File |
Select-Object -ExpandProperty Extension |
Sort-Object -Unique
執行後,會顯示像 .txt、.log、.csv 這樣去除重複後的副檔名。
也可以只看處理程序名稱的種類。
Get-Process |
Select-Object -ExpandProperty ProcessName |
Sort-Object -Unique
從使用者清單 CSV 中,只取出部門名稱清單的範例。
Import-Csv .\users.csv |
Select-Object -ExpandProperty Department |
Sort-Object -Unique
當覺得「清單裡出現太多重複值不好看」時,先想到 Sort-Object -Unique 就會很方便。
8. 依種類統計 ── Group-Object
除了去除重複,如果還想知道「各有幾件」,就用 Group-Object。
依副檔名統計檔案數。
Get-ChildItem . -File |
Group-Object -Property Extension -NoElement |
Sort-Object -Property Count -Descending
加上 -NoElement 後,會省略各群組內容,讓件數更容易閱讀。
依服務狀態統計。
Get-Service |
Group-Object -Property Status -NoElement
依事件記錄檔等級統計。
Get-WinEvent -LogName System -MaxEvents 500 |
Group-Object -Property LevelDisplayName -NoElement
Group-Object 適合在調查一開始用來掌握整體概況。
哪個副檔名最多
哪個狀態的服務最多
哪個錯誤等級最多
哪個部門的資料最多
想在深入細節前先看整體分佈時,可以用它。
9. 建立需要的欄位 ── 新增計算後的欄位
檔案大小可以用 Length 取得,但單位是位元組。每次都看位元組數很辛苦,所以建立換算成 MB 或 KB 的欄位會很方便。
Get-ChildItem . -File |
Select-Object Name, @{Name="SizeKB"; Expression={ [math]::Round($_.Length / 1KB, 1) }}, LastWriteTime
連同子資料夾,依大小由大到小排列的範例。
Get-ChildItem . -File -Recurse |
Sort-Object -Property Length -Descending |
Select-Object -First 20 FullName, @{Name="SizeMB"; Expression={ [math]::Round($_.Length / 1MB, 2) }}, LastWriteTime
@{Name="..."; Expression={ ... }} 是建立計算欄位的寫法。一開始看起來有點長,但實務上會經常用到。
例如,可以用 MB 顯示處理程序的記憶體使用量。
Get-Process |
Sort-Object -Property WorkingSet -Descending |
Select-Object -First 10 Name, Id, @{Name="MemoryMB"; Expression={ [math]::Round($_.WorkingSet / 1MB, 1) }}
要做報告時,SizeMB 這樣的欄位會比原本的 Length 更好讀。
10. 依新舊、大小順序查看
在 PowerShell 中,排序用的是 Sort-Object。
查看最近更新的檔案。
Get-ChildItem . -File |
Sort-Object -Property LastWriteTime -Descending |
Select-Object -First 10 Name, LastWriteTime
查看較舊的檔案。
Get-ChildItem . -File |
Sort-Object -Property LastWriteTime |
Select-Object -First 10 Name, LastWriteTime
查看較大的檔案。
Get-ChildItem . -File -Recurse |
Sort-Object -Property Length -Descending |
Select-Object -First 10 FullName, Length
這 3 種寫法很常用。
新到舊 → LastWriteTime -Descending
舊到新 → LastWriteTime
大到小 → Length -Descending
在記錄檔調查、清理不需要的檔案、磁碟容量調查中,這個組合出場率很高。
11. Format 系列指令只用在畫面顯示的最後一步
想讓畫面顯示更好讀時,用 Format-Table 或 Format-List。
Get-Process |
Sort-Object -Property WorkingSet -Descending |
Select-Object -First 10 Name, Id, WorkingSet |
Format-Table -AutoSize
想看單一項目的細節時,Format-List 很方便。
Get-Process -Id $PID |
Format-List *
不過,Format-Table 和 Format-List 是「顯示專用」。
在要輸出成 CSV 之前,或要交給下一個指令加工之前,最好不要使用它們。
# 避免
Get-Process |
Format-Table Name, Id |
Export-Csv .\process.csv -NoTypeInformation
要輸出成 CSV 的話,用 Select-Object 選好欄位。
# 正確
Get-Process |
Select-Object Name, Id |
Export-Csv .\process.csv -NoTypeInformation -Encoding UTF8
猶豫時,可以這樣想:
只是要在畫面上看 → Format-Table / Format-List
之後還要用 → Select-Object
12. 讓記錄檔搜尋更方便一點 ── Select-String
字串搜尋用 Select-String。
Select-String -Path .\logs\*.log -Pattern "ERROR"
也可以指定多個模式。
Select-String -Path .\logs\*.log -Pattern "ERROR", "WARN", "FATAL"
想連前後行也一起看時,用 -Context。
Select-String -Path .\logs\*.log -Pattern "ERROR" -Context 2
這會連同該行前後 2 行一起顯示,在想確認錯誤前後脈絡時很方便。
想以單純字串來搜尋時,用 -SimpleMatch。
Select-String -Path .\logs\*.log -Pattern "[ERROR]" -SimpleMatch
Select-String 的 -Pattern 預設會當成正規表示式處理。想直接搜尋 [ 或 . 這類符號本身時,加上 -SimpleMatch 會比較清楚。
想區分大小寫的情況。
Select-String -Path .\logs\*.log -Pattern "Error" -CaseSensitive
也可以把搜尋結果留存為 CSV。
Select-String -Path .\logs\*.log -Pattern "ERROR" |
Select-Object Path, LineNumber, Line |
Export-Csv .\error-lines.csv -NoTypeInformation -Encoding UTF8
13. 取代字串
要取代文字內容的一部分時,用 -replace。
"server01.example.com" -replace "\.example\.com$", ""
要取代檔案內容時,直接覆寫原始檔案並不安全,先輸出到另一個檔案。
(Get-Content .\appsettings.json -Raw) -replace "localhost", "db01" |
Set-Content .\appsettings.preview.json -Encoding UTF8
確認過內容後,必要時再反映回原始檔案。
在設定檔或大量取代時,如果取代範圍太廣容易出事故。先用 Select-String 確認目標,再進行取代會比較安全。
Select-String -Path .\appsettings.json -Pattern "localhost" -SimpleMatch
14. 比較兩份結果 ── Compare-Object
Compare-Object 是用來查看兩份清單差異的指令。
例如,比較變更前後的檔名清單。
Compare-Object `
-ReferenceObject (Get-Content .\before.txt) `
-DifferenceObject (Get-Content .\after.txt)
SideIndicator 的意思如下。
| 顯示 | 意義 |
|---|---|
<= |
左側,也就是只存在於 ReferenceObject |
=> |
右側,也就是只存在於 DifferenceObject |
== |
兩邊都有。加上 -IncludeEqual 時會顯示 |
儲存並比較資料夾檔名清單的範例。
Get-ChildItem .\before -File |
Select-Object -ExpandProperty Name |
Set-Content .\before-list.txt -Encoding UTF8
Get-ChildItem .\after -File |
Select-Object -ExpandProperty Name |
Set-Content .\after-list.txt -Encoding UTF8
Compare-Object `
-ReferenceObject (Get-Content .\before-list.txt) `
-DifferenceObject (Get-Content .\after-list.txt)
也可以查看服務清單的變化。
Get-Service |
Select-Object Name, Status |
Export-Csv .\services-before.csv -NoTypeInformation -Encoding UTF8
變更後,再輸出一次。
Get-Service |
Select-Object Name, Status |
Export-Csv .\services-after.csv -NoTypeInformation -Encoding UTF8
進行比較。
Compare-Object `
-ReferenceObject (Import-Csv .\services-before.csv) `
-DifferenceObject (Import-Csv .\services-after.csv) `
-Property Name, Status
保留作業前後的差異,會讓調查與報告更容易進行。
15. 確認檔案的雜湊值 ── Get-FileHash
想確認下載的檔案,或發布的檔案是否一致時,可以用雜湊值。
Get-FileHash .\installer.exe -Algorithm SHA256
一次確認資料夾內所有檔案的情況。
Get-ChildItem .\downloads -File |
ForEach-Object { Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256 }
也可以留存為 CSV。
Get-ChildItem .\downloads -File |
ForEach-Object { Get-FileHash -LiteralPath $_.FullName -Algorithm SHA256 } |
Select-Object Path, Hash |
Export-Csv .\hashes.csv -NoTypeInformation -Encoding UTF8
只看檔名,有時會覺得檔案相同,但內容其實不一樣。在發布物、備份、遷移作業中,留下雜湊值會讓確認更容易。
16. 傳送到剪貼簿 ── Set-Clipboard
想把指令結果貼到郵件或聊天工具時,傳送到剪貼簿很方便。
Get-Service |
Select-Object Name, Status |
ConvertTo-Csv -NoTypeInformation |
Set-Clipboard
查看剪貼簿內容的情況。
Get-Clipboard
想貼到 Excel 而需要用 Tab 分隔時,把 ConvertTo-Csv 的分隔字元改成 Tab。
Get-Process |
Select-Object Name, Id, CPU |
ConvertTo-Csv -NoTypeInformation -Delimiter "`t" |
Set-Clipboard
只是想暫時分享結果的話,這比每次都建立檔案輕鬆。但如果想留下紀錄,就用 Export-Csv 儲存成檔案。
17. 同時顯示在畫面上並留存到檔案 ── Tee-Object
想邊看畫面上的結果,又同時留存到檔案時,用 Tee-Object。
Get-Process |
Sort-Object -Property WorkingSet -Descending |
Select-Object -First 10 Name, Id, WorkingSet |
Tee-Object -FilePath .\top-process.txt
Tee-Object 會把管線的結果分成兩個方向輸出。
顯示在畫面上
同時儲存到檔案
適合用在調查過程中「邊看邊記錄」的用途。不過,之後想用 Excel 開啟的話,Export-Csv 會更好處理。
Get-Process |
Sort-Object -Property WorkingSet -Descending |
Select-Object -First 10 Name, Id, WorkingSet |
Export-Csv .\top-process.csv -NoTypeInformation -Encoding UTF8
畫面用就想成 Tee-Object,要留存成表格資料就想成 Export-Csv,這樣會比較清楚。
18. 留存工作紀錄 ── Start-Transcript
想記錄一連串的操作時,用 Start-Transcript。
Start-Transcript -Path .\work-log.txt
之後執行的指令與畫面輸出都會被記錄下來。
結束記錄。
Stop-Transcript
在調查作業中,之後常常會想確認以下這些事:
執行過哪些指令
在哪個資料夾作業
出現了什麼結果
有沒有出現錯誤
使用 Start-Transcript,可以在忘記做筆記時當作一種保險。不過,在畫面上會出現密碼或 token 等不該被記錄的資訊的作業中,要特別注意使用方式。
19. 讀取 CSV 並依條件查看
在業務資料中,CSV 經常出現。
假設有以下這樣的 users.csv。
Name,Department,Enabled
Suzuki,Sales,true
Tanaka,Accounting,false
Sato,Sales,true
讀取它。
Import-Csv .\users.csv
只查看部門為 Sales 的人。
Import-Csv .\users.csv |
Where-Object { $_.Department -eq "Sales" }
只把啟用中的使用者輸出到另一個 CSV。
Import-Csv .\users.csv |
Where-Object { $_.Enabled -eq "true" } |
Export-Csv .\enabled-users.csv -NoTypeInformation -Encoding UTF8
CSV 的值大多會被當成字串處理,所以要比較 true / false 或數值時,需要視情況考慮型別轉換。
例如,只查看分數 80 以上的行的情況。
Import-Csv .\scores.csv |
Where-Object { [int]$_.Score -ge 80 }
20. 查看 JSON 的設定值
JSON 經常用在設定檔或 Web API 中。
$config = Get-Content .\settings.json -Raw | ConvertFrom-Json
$config
也可以只查看特定的值。
$config.Database.Host
變更值後儲存到另一個檔案的範例。
$config.Database.Host = "db01"
$config |
ConvertTo-Json -Depth 10 |
Set-Content .\settings.updated.json -Encoding UTF8
處理 JSON 時,常常會用 Get-Content -Raw。加上 -Raw 會把整個檔案讀成一個字串,處理多行 JSON 時會更清楚易懂。
21. 查看環境變數
環境變數常用在應用程式設定或路徑確認上。
查看清單。
Get-ChildItem Env:
查看目前的使用者名稱。
$env:USERNAME
查看暫存資料夾。
$env:TEMP
把 PATH 拆開來看,會更好讀。
$env:PATH -split ";"
只在目前這個 PowerShell 工作階段中設定環境變數。
$env:APP_MODE = "Development"
這個設定只在目前開啟的這個 PowerShell 中有效。
想儲存為使用者環境變數時,這樣做。
[Environment]::SetEnvironmentVariable("APP_MODE", "Development", "User")
這是變更環境的操作,在公司電腦或正式伺服器上,要先確認影響範圍再進行。
22. 確認設定檔(Profile)
PowerShell 有一個啟動時會載入的設定檔(Profile)。
確認位置。
$PROFILE
確認是否存在。
Test-Path $PROFILE
建立設定檔的情況。
New-Item -ItemType File -Path $PROFILE -Force
用記事本開啟。
notepad $PROFILE
把常用的設定或函式寫進設定檔會很方便。不過,如果寫太多複雜的處理,會讓 PowerShell 啟動變慢,也不容易在其他環境重現,所以一開始建議只放常用的路徑或顯示設定就好。
23. 查看處理程序
調查處理程序時,通常會先依記憶體使用量由大到小排列查看。
Get-Process |
Sort-Object -Property WorkingSet -Descending |
Select-Object -First 10 Name, Id, @{Name="MemoryMB"; Expression={ [math]::Round($_.WorkingSet / 1MB, 1) }}
依特定名稱篩選。
Get-Process -Name notepad -ErrorAction SilentlyContinue
要停止處理程序時,不要一開始就直接執行,先用 -WhatIf 確認。
Stop-Process -Name notepad -WhatIf
沒問題的話再執行。
Stop-Process -Name notepad
在正式環境中,停止處理程序有時會影響業務運作。在停止之前,要先確認對象、使用者,以及復原方法。
24. 查看服務
查看服務清單。
Get-Service
只查看已停止的服務的情況。
Get-Service |
Where-Object { $_.Status -eq "Stopped" }
尋找設定為自動啟動卻停止的服務。
Get-Service |
Where-Object { $_.StartType -eq "Automatic" -and $_.Status -ne "Running" } |
Select-Object Name, DisplayName, Status, StartType
要重新啟動服務時,也要先確認對象。
Get-Service -Name "Spooler"
先模擬一次。
Restart-Service -Name "Spooler" -WhatIf
服務操作的影響範圍較大,要先確認維護時間與復原步驟後才執行。
25. 查看事件記錄檔
在 Windows 的調查中,常常需要查看事件記錄檔。
查看系統記錄檔最近的 100 筆。
Get-WinEvent -LogName System -MaxEvents 100 |
Select-Object TimeCreated, ProviderName, Id, LevelDisplayName, Message
只篩選錯誤與警告。
Get-WinEvent -LogName System -MaxEvents 500 |
Where-Object { $_.LevelDisplayName -in @("Error", "Warning") } |
Select-Object TimeCreated, ProviderName, Id, LevelDisplayName, Message
只查看最近 24 小時內的錯誤。
$start = (Get-Date).AddHours(-24)
Get-WinEvent -FilterHashtable @{ LogName = "System"; StartTime = $start } |
Where-Object { $_.LevelDisplayName -eq "Error" } |
Select-Object TimeCreated, ProviderName, Id, 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
事件記錄檔容易變得件數龐大,用 -MaxEvents 或指定期間來縮小範圍很重要。
26. 確認網路
確認連通性時用 Test-Connection。
Test-Connection example.com -Count 4
在 Windows 上想確認 TCP 埠是否連通時,Test-NetConnection 很方便。
Test-NetConnection example.com -Port 443
確認 DNS 解析的情況。
Resolve-DnsName example.com
在網路調查中,會依下面的順序拆解問題:
是否能解析名稱
能否連到 IP
目標埠是否開啟
是否受 Proxy 或防火牆影響
不要試圖只靠 PowerShell 判斷全部,把它當成拆解問題的入口會更好用。
27. 查看 Web 的結果
要取得網頁時,用 Invoke-WebRequest。
Invoke-WebRequest https://example.com |
Select-Object StatusCode, StatusDescription
要處理 JSON API 的結果時,Invoke-RestMethod 很方便。
Invoke-RestMethod https://api.github.com/repos/PowerShell/PowerShell |
Select-Object full_name, stargazers_count, forks_count
API 的結果大多一開始就能當成 PowerShell 物件處理,容易和 Select-Object、Where-Object 搭配使用。
調查內部 API 時,也建議先從讀取類的 GET 開始。
處理認證資訊或 token 時,要注意畫面顯示以及是否會被記錄到 transcript 中。
28. 測量執行時間 ── Measure-Command
想知道處理需要多少時間時,用 Measure-Command。
Measure-Command {
Get-ChildItem C:\Windows -File -Recurse -ErrorAction SilentlyContinue |
Measure-Object
}
結果中會包含 TotalSeconds 或 TotalMilliseconds。
$result = Measure-Command {
Get-ChildItem . -File -Recurse | Measure-Object
}
$result.TotalSeconds
想讓腳本變快時,比起憑感覺,實際測量時間才是重點。不過,結果會因檔案數量、磁碟狀態、網路狀態而不同,不要只憑一次的結果判斷,要多確認幾次。
29. 讓耗時處理在背景執行 ── Start-Job
想把耗時的調查工作,當作另一個工作在背景執行時,用 Start-Job。
$job = Start-Job -ScriptBlock {
Get-ChildItem C:\Windows -File -Recurse -ErrorAction SilentlyContinue |
Measure-Object
}
查看工作的狀態。
Get-Job
接收結果。
Receive-Job $job
刪除不再需要的工作。
Remove-Job $job
Start-Job 很方便,但不需要一開始就大量使用。先照一般方式執行,只把耗時的處理改成工作化,這樣會比較容易理解。
30. 使用指令歷史記錄
要查看過去執行過的指令時,用 Get-History。
Get-History
只讓指令列更好讀。
Get-History |
Select-Object Id, CommandLine
重新執行特定的歷史記錄。
Invoke-History 12
歷史記錄很方便,但也可能因此重新執行危險的指令。從歷史記錄重新執行 Remove-Item 或 Stop-Process 這類變更類指令時,要先確認內容再執行。
31. 查看錯誤細節 ── Get-Error
在 PowerShell 7 中,可以用 Get-Error 詳細查看最近發生的錯誤。
Get-Error
只想看最近一次錯誤訊息的情況。
$Error[0].Exception.Message
發生錯誤時,不要只看畫面上的紅字,還要確認以下這些重點:
是哪個指令失敗
在哪個路徑或對象上失敗
是不是權限問題
是不是檔案不存在
是不是網路或鎖定問題
想留存調查結果時,可以搭配 Start-Transcript 或 Export-Csv 使用。
32. 常用的比較運算子
在 Where-Object 中,比較運算子非常常用。
| 運算子 | 意義 | 範例 |
|---|---|---|
-eq |
相等 | $_.Status -eq "Running" |
-ne |
不相等 | $_.Status -ne "Running" |
-gt |
大於 | $_.Length -gt 10MB |
-ge |
大於等於 | $_.Length -ge 10MB |
-lt |
小於 | $_.Length -lt 10MB |
-le |
小於等於 | $_.Length -le 10MB |
-like |
萬用字元比對 | $_.Name -like "*.log" |
-match |
正規表示式比對 | $_.Name -match "^app" |
-in |
值存在於清單中 | $_.Status -in @("Running", "Paused") |
-contains |
清單包含該值 | @("a", "b") -contains "a" |
範例如下。
Get-ChildItem . -File |
Where-Object { $_.Length -gt 10MB }
Get-Service |
Where-Object { $_.Status -in @("Running", "Paused") }
Get-ChildItem . -File |
Where-Object { $_.Name -match "\.log$" }
一開始只用 -eq、-ne、-gt、-lt、-like 就已經很夠用。熟悉之後,善用 -match 或 -in 可以讓條件更容易寫。
33. 幾個實用的實務範例
接下來是一些可以直接套用在實務上的範例。
查看各副檔名的檔案數
Get-ChildItem C:\Work -File -Recurse |
Group-Object -Property Extension -NoElement |
Sort-Object -Property Count -Descending
用於想大致掌握資料夾內容時。
查看容量前 20 大的檔案
Get-ChildItem C:\Work -File -Recurse |
Sort-Object -Property Length -Descending |
Select-Object -First 20 FullName, @{Name="SizeMB"; Expression={ [math]::Round($_.Length / 1MB, 2) }}
常用於磁碟容量調查。
查看最近 24 小時內更新過的檔案
$since = (Get-Date).AddHours(-24)
Get-ChildItem C:\Work -File -Recurse |
Where-Object { $_.LastWriteTime -ge $since } |
Select-Object FullName, Length, LastWriteTime
用於故障發生後,查看哪些檔案被更新過。
把包含 ERROR 的記錄行輸出成 CSV
Select-String -Path C:\App\Logs\*.log -Pattern "ERROR" |
Select-Object Path, LineNumber, Line |
Export-Csv .\error-lines.csv -NoTypeInformation -Encoding UTF8
方便分享調查結果。
查看 ERROR 前後 2 行
Select-String -Path C:\App\Logs\*.log -Pattern "ERROR" -Context 2
用於想查看原因前後脈絡時。
查看記憶體使用量較大的處理程序
Get-Process |
Sort-Object -Property WorkingSet -Descending |
Select-Object -First 10 Name, Id, @{Name="MemoryMB"; Expression={ [math]::Round($_.WorkingSet / 1MB, 1) }}
查看設定為自動啟動卻停止的服務
Get-Service |
Where-Object { $_.StartType -eq "Automatic" -and $_.Status -ne "Running" } |
Select-Object Name, DisplayName, Status, StartType
把系統記錄檔的錯誤輸出成 CSV
Get-WinEvent -LogName System -MaxEvents 500 |
Where-Object { $_.LevelDisplayName -eq "Error" } |
Select-Object TimeCreated, ProviderName, Id, Message |
Export-Csv .\system-errors.csv -NoTypeInformation -Encoding UTF8
把指令結果傳送到剪貼簿
Get-Service |
Select-Object Name, DisplayName, Status |
ConvertTo-Csv -NoTypeInformation |
Set-Clipboard
方便貼到郵件或聊天工具中。
比較作業前後的檔案清單
Get-ChildItem C:\Work -File |
Select-Object -ExpandProperty Name |
Set-Content .\before.txt -Encoding UTF8
作業結束後再取一次。
Get-ChildItem C:\Work -File |
Select-Object -ExpandProperty Name |
Set-Content .\after.txt -Encoding UTF8
進行比較。
Compare-Object (Get-Content .\before.txt) (Get-Content .\after.txt)
用 GB 單位查看資料夾的總容量
$size = Get-ChildItem C:\Work -File -Recurse |
Measure-Object -Property Length -Sum
[math]::Round($size.Sum / 1GB, 2)
34. 搭配變更類指令時的注意事項
到目前為止的大部分內容都是讀取類指令,光用這些就可以比較放心地嘗試。
另一方面,下面這些是變更類指令。
| 操作 | 指令 |
|---|---|
| 複製 | Copy-Item |
| 移動 | Move-Item |
| 刪除 | Remove-Item |
| 更名 | Rename-Item |
| 停止處理程序 | Stop-Process |
| 重新啟動服務 | Restart-Service |
| 永久設定環境變數 | [Environment]::SetEnvironmentVariable() |
變更類指令要依下面的順序使用:
1. 用 Get 系列指令查看對象
2. 用 Where-Object 篩選
3. 用 Select-Object 查看目標清單
4. 用 Measure-Object 數清件數
5. 必要時用 Export-Csv 留存紀錄
6. 若指令支援 -WhatIf,先模擬一次
7. 執行
例如,刪除舊的 .tmp 檔案的情況。
先查看對象。
$limit = (Get-Date).AddDays(-30)
$targets = Get-ChildItem C:\Temp -Filter *.tmp -File -Recurse |
Where-Object { $_.LastWriteTime -lt $limit }
$targets |
Select-Object FullName, Length, LastWriteTime
數清件數。
$targets | Measure-Object
留存為 CSV。
$targets |
Select-Object FullName, Length, LastWriteTime |
Export-Csv .\delete-targets.csv -NoTypeInformation -Encoding UTF8
確認要刪除的內容。
$targets | Remove-Item -WhatIf
沒問題的話再執行。
$targets | Remove-Item
只要守住這個流程,就能大幅減少操作失誤。
35. 記憶方法
一個個死記指令會很累,所以依目的來記反而更好用。
| 目的 | 該想起的指令 |
|---|---|
| 想數件數 | Measure-Object |
| 想加總 | Measure-Object -Sum |
| 想依種類查看 | Group-Object |
| 想去除重複 | Sort-Object -Unique |
| 只想要需要的值 | Select-Object -ExpandProperty |
| 想整理欄位 | Select-Object |
| 想依大小排序查看 | Sort-Object -Descending |
| 想搜尋字串 | Select-String |
| 想看前後行 | Select-String -Context |
| 想比較兩者 | Compare-Object |
| 想留下紀錄 | Export-Csv / Start-Transcript |
| 想同時輸出到畫面與檔案 | Tee-Object |
| 想傳送到剪貼簿 | Set-Clipboard |
| 想測量時間 | Measure-Command |
| 想詳細查看錯誤 | Get-Error |
36. 結語
PowerShell 學會寫腳本之後會非常強大。不過,實務上用得最多的,意外地反而是這些小指令的組合。
數
排序
彙整
比較
搜尋
取出
儲存
傳送到剪貼簿
留存工作紀錄
只要能做到這些,日常工作就會輕鬆許多。不需要一開始就追求困難的自動化。
先用讀取類指令準確掌握現況,然後在真正需要的時候,才使用 Copy-Item、Move-Item、Remove-Item、Restart-Service 這類變更類指令。
安全使用 PowerShell 的秘訣,就是照下面的順序來:
查看 → 數件數 → 整理 → 留存紀錄 → 模擬 → 執行
養成這個習慣之後,PowerShell 就不再只是「在黑色畫面上打指令的東西」,而會成為支撐日常調查、整理、報告工作的實用工具。
相關文章
共用相同標籤的最新文章。能以相近的主題延伸理解。
PowerShell 指令基礎 ── 該先學會的操作與安全使用方式
為了讓 PowerShell 初學者在實務上不會迷路,本文整理了如何尋找 Cmdlet、管線、檔案操作、CSV 處理、執行原則,以及避免事故的確認步驟。
在 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-ChildItem 的結果傳給 Measure-Object 即可。件數可以從結果的 Count 屬性確認,如果要連子資料夾也一起算,加上 -Recurse。要統計總容量的話,用 Measure-Object -Property Length -Sum 來加總,因為直接看位元組數不好讀,所以像 [math]::Round($size.Sum / 1GB, 2) 這樣換算成 GB 單位會更好看。再加上 -Average 或 -Maximum,也可以一次算出平均值與最大值。
- Select-Object 的 -ExpandProperty 跟一般的指定方式有什麼不同?
- Select-Object Name 回傳的是「帶有 Name 這個欄位的表格(物件)」,而 Select-Object -ExpandProperty Name 回傳的則是「Name 的值本身」。想輸出成 CSV 時,保留欄位形式的一般指定比較合適;如果要把值傳給其他指令、做字串處理,或只想把值寫進文字檔,用 -ExpandProperty 會比較好處理。依用途區分使用即可。
- 為什麼不能在 Export-Csv 之前使用 Format-Table?
- 因為 Format-Table 和 Format-List 是給畫面顯示用的指令,輸出會變成顯示專用的物件,導致之後的 CSV 輸出或加工無法正確進行。如果要輸出成 CSV 或交給下一個指令加工,應該先用 Select-Object 選好欄位,再傳給 Export-Csv。記住「只是要看畫面就用 Format 系列,之後還要用就用 Select-Object」,就不會搞混。
- 有沒有安全使用 Remove-Item 等變更類指令的步驟?
- 不要一開始就直接執行,先用 Get 系列指令查看對象,再用 Where-Object 篩選,用 Select-Object 確認目標清單,用 Measure-Object 數清楚件數,必要時用 Export-Csv 留下紀錄。做完這些之後,如果指令支援 -WhatIf,就先模擬一次確認要刪除或搬移的內容,沒問題才正式執行。只要守住這個順序,就能大幅減少操作失誤。
作者檔案
本文作者的個人檔案頁面。
Go Komura
小村軟體有限公司 代表
以 Windows 軟體開發、技術諮詢與故障調查為中心,在難以重現的故障調查與既有資產仍在運作的專案上具有優勢。