Skip to content

PowerShell命令行编程艺术(GitHub Copilot版)

本指南面向想用 PowerShell 做日常自动化、系统管理、开发辅助以及脚本编写的读者。内容从基础到高级,并包含实用技巧、常见陷阱与最佳实践示例。文中示例以 PowerShell 7+(跨平台)为主,同时兼顾 Windows PowerShell(5.1)差异提示。

兼容性注记

  • 本文默认以 PowerShell 7+(Core)为准。Windows PowerShell 5.1 与 7+ 在一些命令参数、编码默认值与并行处理特性上存在差异,例如 ForEach-Object -Parallel 仅在 PowerShell 7+ 可用;PowerShell Core 的默认编码为 UTF-8,而 5.1 仍使用 UTF-16/ANSI(取决于上下文)。在撰写脚本时,请标注目标 PowerShell 版本或使用兼容性检查。

PowerShell 基础

交互式使用

PowerShell 是基于对象的命令行外壳。与传统的文本流不同,PowerShell 在管道中传递 .NET 对象。

示例:列出当前目录并按大小排序:

Get-ChildItem -File | Sort-Object Length -Descending | Select-Object -First 10

要快速查看某一命令的参数与帮助:

Get-Help Get-ChildItem -Full

如果帮助未安装,先运行:

Update-Help -Force

对象管道与管道传输

管道中传递的是对象,不是文本,这允许直接访问属性:

Get-Process | Where-Object { $_.CPU -gt 10 } | Select-Object Name, Id, CPU

使用 Select-Object、Select-String、Format-Table 等工具在不同场景下选择和格式化输出。

常用命令与别名

  • ls / dir -> Get-ChildItem
  • cat -> Get-Content
  • ps -> Get-Process
  • rm -> Remove-Item

使用 Get-Command -Module <模块名> 查看模块命令。

变量、类型与强制转换

变量以 $ 开头:$name = 'xue'

强制类型转换示例:

[int]$n = "42"

注意:自动类型推断有时会导致数组或单个元素的差异,使用 , 运算符保证数组:

$a = ,(Get-ChildItem | Where-Object { $_.PSIsContainer })

高级功能与脚本化

模块与函数设计

  • 编写模块(*.psm1)来封装函数。
  • 使用 Export-ModuleMember 指定导出函数。

示例函数模板:

function Get-MyData {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true, Position=0)]
        [string]$Path,

        [switch]$Recurse
    )

    process {
        Get-ChildItem -Path $Path -File -Recurse:$Recurse
    }
}

参数绑定与参数集

使用 Parameter 属性控制参数绑定、位置、别名与参数集。参数集允许在同一函数中定义互斥的参数组合。

异常处理与调试

  • 使用 try/catch/finally 捕获异常。
  • 使用 -ErrorAction Stop 将非终止错误提升为异常。
  • 使用 Write-Debug / Write-Verbose 提供可选诊断输出。

示例:

try {
    Get-Content missing.file -ErrorAction Stop
} catch {
  # 捕获时 $_ 是 ErrorRecord,优先尝试输出异常信息
  Write-Warning "读取文件失败: $($_.Exception.Message)"
} finally {
    Write-Verbose "清理完成"
}

异步/并行:Jobs、Runspaces、ForEach-Object -Parallel

  • Start-Job / Receive-Job:适用于后台任务,但与当前会话对象不可直接共享。
  • ForEach-Object -Parallel(PowerShell 7+)提供内置并行处理。

示例并行处理(PowerShell 7+):

$items = 1..10
$items | ForEach-Object -Parallel {
  # 并行代码块:管道当前项可通过 $_ 访问;若要在并行脚本块中使用外部变量,请使用 $using: 前缀
  Write-Output "处理: $_ (运行在 $env:COMPUTERNAME)"
} -ThrottleLimit 4

注意:并行脚本块在独立的子进程中执行,管道项通过 $_ 可用;若需引用函数外部的变量或对象,请使用 $using:VarName 前缀。此外,可通过 -ThrottleLimit 控制并行度。

系统与文件操作

文件、目录、权限管理

  • Get-Acl / Set-Acl 管理 NTFS 权限。
  • 使用 Test-Path 检查存在性。
  • 推荐使用 Join-Path 构造路径以跨平台兼容。

示例:安全地创建目录:

$dir = Join-Path $env:USERPROFILE 'mydata'
if (-not (Test-Path $dir)) { New-Item -ItemType Directory -Path $dir }

注册表与服务控制

  • 使用 Get-ItemProperty / Set-ItemProperty 操作注册表(Windows)。
  • 使用 Get-Service / Start-Service / Stop-Service 管理服务。

进程与性能监控

  • Get-Process、Stop-Process、Measure-Command(测量执行时间)。

示例测量命令耗时:

Measure-Command { Get-ChildItem -Recurse | Out-Null }

网络与 HTTP 自动化

Invoke-WebRequest / Invoke-RestMethod

  • Invoke-RestMethod 会自动将 JSON 转换为对象,适合 API 使用。

示例 GET 请求:

$resp = Invoke-RestMethod -Uri 'https://api.github.com/repos/qingtk/xue' -Headers @{ 'User-Agent' = 'PowerShell' }
$resp.full_name

示例 POST JSON(推荐在脚本中使用 SecretManagement 存储令牌,或从环境变量读取):

$body = @{ title = '自动化 Issue'; body = '由 PowerShell 创建' } | ConvertTo-Json
Invoke-RestMethod -Uri 'https://api.github.com/repos/<owner>/<repo>/issues' -Method Post -Body $body -ContentType 'application/json' -Headers @{ Authorization = "Bearer $token"; 'User-Agent' = 'PowerShell'; 'Accept' = 'application/vnd.github+json' }

注意处理速率限制与身份验证。示例脚本见 ./scripts/create-github-issue.ps1,建议结合 Microsoft.PowerShell.SecretManagement 存储与读取 GitHub 令牌。

与 Git / GitHub 的集成

  • 直接在脚本中调用 git(CLI)来提交、推送。
  • 使用 GitHub REST API 或 GraphQL 进行自动化任务(需要令牌)。

示例获取当前分支名:

(git rev-parse --abbrev-ref HEAD).Trim()

示例脚本:./scripts/create-github-issue.ps1(演示如何结合 SecretManagement 创建 Issue)。

PowerShell 实用技巧

交互友好输出

  • 使用 Out-GridView (Windows) 进行图形化快速选择。
  • 使用 Format-Table -AutoSize 或 Format-List 以更好的控制显示。

读写 JSON、YAML、CSV

  • ConvertTo-Json / ConvertFrom-Json
  • 使用 PowerShellYAML 模块读取 YAML(第三方)
  • Import-Csv / Export-Csv 操作表格数据

示例保存对象为 JSON:

Get-Process | Select-Object Name,Id,CPU | ConvertTo-Json -Depth 3 | Set-Content -Path processes.json -Encoding utf8

使用 SecretManagement 存储敏感信息

  • Microsoft.PowerShell.SecretManagement 与 SecretStore 提供跨会话的安全存储。

安装并注册本地存储:

Install-Module Microsoft.PowerShell.SecretManagement -Scope CurrentUser
Install-Module Microsoft.PowerShell.SecretStore -Scope CurrentUser
Register-SecretVault -Name LocalStore -ModuleName Microsoft.PowerShell.SecretStore -DefaultVault
Set-Secret -Name GitHubToken -Secret (Read-Host -AsSecureString)

读取:

$token = Get-Secret -Name GitHubToken

常用正则与文本处理

  • 使用 -match、-replace、Select-String 进行文本匹配。

示例提取电子邮件:

'Contact: foo@example.com' -match '\b[\w.-]+@[\w.-]+\.\w{2,}\b'
$matches[0]

与其他工具的互操作

  • 使用 Start-Process 启动外部程序并控制参数。
  • 在脚本中调用 dotnet、python、node 等工具。

在 GitHub Actions 中使用 PowerShell(示例):

- name: Run PowerShell script
  shell: pwsh
  run: |
    ./scripts/ci.ps1

性能与最佳实践

  • 避免在循环中重复使用外部命令,尽量先收集数据再批量处理。
  • 使用 pipeline-friendly 的函数,输出对象而非打印字符串。
  • 为脚本添加日志与重试逻辑。

示例重试封装:

function Invoke-WithRetry {
    param(
        [scriptblock]$ScriptBlock,
        [int]$MaxAttempts = 3,
        [int]$DelaySeconds = 2
    )

    for ($i = 1; $i -le $MaxAttempts; $i++) {
        try {
            return & $ScriptBlock
        } catch {
            if ($i -eq $MaxAttempts) { throw }
            Start-Sleep -Seconds $DelaySeconds
        }
    }
}

常见陷阱与注意事项

  • 执行策略(ExecutionPolicy)并不是安全边界;它只用于防止意外执行脚本。
  • 路径分隔符在 Windows 与 Unix 上不同,优先使用 Join-Path 或 [IO.Path]::Combine。
  • 字符编码:PowerShell Core 默认为 UTF-8,而 Windows PowerShell 使用 ANSI/UTF-16,注意文件读写编码。
  • 当在管理员权限下运行脚本时,避免在无意中改写系统配置。

附录:常用一行命令速查表

  • 查找文件包含关键字:
Select-String -Path .\*.ps1 -Pattern 'TODO' -List
  • 计算文件夹大小:
Get-ChildItem -Recurse | Measure-Object -Property Length -Sum
  • 批量替换文本:
Get-ChildItem -Recurse -Filter *.txt | ForEach-Object { (Get-Content $_.FullName) -replace 'foo','bar' | Set-Content $_.FullName }

参考资料与进一步阅读


作者: 借助 GitHub Copilot 助力编写