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
目标端口是否开放
是否受代理或防火墙影响

不要试图只靠 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 软件开发、技术咨询与故障排查为中心,擅长难以复现的故障调查,以及既有资产仍在运行的项目。

返回博客列表