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
目标端口是否开放
是否受代理或防火墙影响
不要试图只靠 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 宏与企业内部工具的资产排查、静态检测、执行日志采集、替代技术选型、测试与分阶段推广的做法。
常见问题
汇总了咨询这一主题时常见的问题。
- 在 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 软件开发、技术咨询与故障排查为中心,擅长难以复现的故障调查,以及既有资产仍在运行的项目。