PowerShell 實用指令集錦 ── 累積日常工作常用的小工具

· · PowerShell, Windows, 命令列, 自動化, 業務效率化, 既有資產活用

1. 首先要掌握的事

學會 PowerShell 的基礎之後,接下來要做的是累積「常用的小工具」,這樣在實務上會更好用。

這裡說的小工具,並不是指簡短的別名。這篇文章不會用 lscat 這類簡寫,而是使用 Get-ChildItemGet-ContentSelect-String 這類正式的指令名稱。

在此基礎上,讓下面這些工作都能用一行或幾行指令完成:

數件數
查總容量
去除重複
依種類統計
只取出需要的值
比較兩份結果
查看記錄檔前後幾行
傳送到剪貼簿
儲存執行結果的同時也顯示在畫面上
確認環境變數與網路狀態

PowerShell 不需要寫很長的腳本也能派上用場。先累積「常用的零件」,再在日常工作中逐步組合運用。

與其死記指令,更重要的是能想起「這種情況可以用這個零件」。

順帶一提,本文出現的程式碼,已依章節主題整理成一套可直接執行的範例(含 recipe 腳本與 Pester 測試),發表在 GitHub 上。

powershell-practical-command-recipes - komurasoft-blog-samples (GitHub)

2. 這次的思考方式

前提有以下 3 點:

  1. 指令名稱用正式全名書寫
  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-ProcessCPU 不是使用率,而是處理程序啟動以來累積使用的 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 NameSelect-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-TableFormat-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-TableFormat-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-ObjectWhere-Object 搭配使用。

調查內部 API 時,也建議先從讀取類的 GET 開始。

處理認證資訊或 token 時,要注意畫面顯示以及是否會被記錄到 transcript 中。

28. 測量執行時間 ── Measure-Command

想知道處理需要多少時間時,用 Measure-Command

Measure-Command {
  Get-ChildItem C:\Windows -File -Recurse -ErrorAction SilentlyContinue |
    Measure-Object
}

結果中會包含 TotalSecondsTotalMilliseconds

$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-ItemStop-Process 這類變更類指令時,要先確認內容再執行。

31. 查看錯誤細節 ── Get-Error

在 PowerShell 7 中,可以用 Get-Error 詳細查看最近發生的錯誤。

Get-Error

只想看最近一次錯誤訊息的情況。

$Error[0].Exception.Message

發生錯誤時,不要只看畫面上的紅字,還要確認以下這些重點:

是哪個指令失敗
在哪個路徑或對象上失敗
是不是權限問題
是不是檔案不存在
是不是網路或鎖定問題

想留存調查結果時,可以搭配 Start-TranscriptExport-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-ItemMove-ItemRemove-ItemRestart-Service 這類變更類指令。

安全使用 PowerShell 的秘訣,就是照下面的順序來:

查看 → 數件數 → 整理 → 留存紀錄 → 模擬 → 執行

養成這個習慣之後,PowerShell 就不再只是「在黑色畫面上打指令的東西」,而會成為支撐日常調查、整理、報告工作的實用工具。

共用相同標籤的最新文章。能以相近的主題延伸理解。

與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。

本文連結到以下服務頁面,歡迎從最接近的入口查看。

常見問題

整理諮詢這個主題時常見的問題。

在 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 軟體開發、技術諮詢與故障調查為中心,在難以重現的故障調查與既有資產仍在運作的專案上具有優勢。

回到部落格一覽