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
根据环境不同,这可能需要网络连接与相应权限。在公司电脑上,也可能因为代理或管理策略而失败。
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。
相关文章
共享相同标签的最新文章。可以围绕相近的主题进一步加深理解。
用 Pester 完善 PowerShell 测试 ── 让运维脚本不易损坏的实务方法
本文整理用 Pester v5 测试 PowerShell 脚本的实务步骤,涵盖日期处理、文件操作、删除处理、Mock,一直到 CI 执行,帮助安全地打好测试基础。
PowerShell 实用命令合集 —— 积累日常工作中常用的小工具
本文整理 PowerShell 日常工作中常用的实用命令,说明 Measure-Object、Group-Object、Select-String、Compare-Object、Tee-Object、Start-Transcript 等命令的使用场景和时机。
PowerShell 脚本进阶 ── 安全地实现日志排查、归档与报表自动化
本文整理了用 PowerShell 脚本安全推进日志排查、CSV 报表、旧日志归档、留存记录,直至任务计划程序执行的实务步骤。
在 C#(CSharp)中执行 PowerShell 并以对象形式接收结果
本文从实务角度整理如何从 C# 启动 PowerShell,并以 PSObject 而非字符串来接收结果,涵盖 PowerShell SDK、AddCommand、AddParameter、BaseObject、Properties 直至错误处理。
任务计划程序的任务不执行、以 0x1 结束 ── 原因排查与安全的运维设计
本文整理 Windows 任务计划程序的执行账户与登录类型、「不管用户是否登录都要运行」的含义、以 0x1 结束的典型原因、启用历史记录、防止多重启动、PowerShell 脚本的正确调用方式,直到把定期执行真正落实到运维上的设计方针。
常见问题
汇总了咨询这一主题时常见的问题。
- 学习 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 软件开发、技术咨询与故障排查为中心,擅长难以复现的故障调查,以及既有资产仍在运行的项目。