mcpField
P
powershell-safe-invocation

powershell-safe-invocation

github.com/misaka-mikoto-tech开发28263 次安装5 周前更新
代码仓库

关于 powershell-safe-invocation

面向需要安全调用本机程序和管理文件的开发者与自动化脚本作者,提供 PowerShell 使用指南。内容涵盖参数传递、引号、编码、Shell 版本差异及执行方式选择。

主要功能

  1. 01结构化传递本机程序参数
  2. 02安全引号与脚本文件使用指南
  3. 03使用哈希表传递 Cmdlet 参数
  4. 04注意编码的文件处理
  5. 05检查 Shell 版本和命令语法

适用场景

  1. 01启动路径含空格的本机程序
  2. 02运行多行或嵌套引号脚本
  3. 03保留文件编码地进行处理

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/misaka-mikoto-tech/agent-skills --skill powershell-safe-invocation

安装后在 Claude Code 中输入/powershell-safe-invocation确认已加载。

附带文件reference.md

详细介绍

PowerShell 安全调用

Shell

除非明确要求使用 Windows PowerShell 5.1,否则请通过 pwsh.exe 使用 PowerShell 7。

如果不确定当前使用的 Shell,请进行验证:

$PSVersionTable.PSVersion
$PSNativeCommandArgumentPassing

不要假设安装 PowerShell 7 后,powershell.exe 就会使用 PowerShell 7:

  • pwsh.exe = PowerShell 7
  • powershell.exe = Windows PowerShell 5.1

原生程序

如果可以分别传递参数,切勿将所有参数拼成一个命令字符串。

请使用:

$exe = 'C:\Path With Spaces\tool.exe'
$argList = @(
    '--input'
    'C:\Data Folder\input.json'
    '--flag'
)

& $exe @argList

$exitCode = $LASTEXITCODE
if ($exitCode -ne 0) {
    throw "$exe failed with exit code $exitCode"
}

规则:

  • 将每个原生参数视为一个数组项。
  • 使用 & 调用存储在变量中的可执行文件路径。
  • 立即捕获 $LASTEXITCODE。
  • 避免将 $args 用作自己的参数数组名称;它是 PowerShell 自动变量。请使用 $argList 或 $nativeArgs 等名称。
  • 不要使用 Invoke-Expression。
  • 不要仅为了启动可执行文件而添加 cmd.exe /c 层。
  • 不要在 PowerShell 中使用 Bash 风格的 \" 转义。

Cmdlet

对 PowerShell cmdlet 使用哈希表展开传参:

$params = @{
    LiteralPath = 'C:\Data[1]\input.txt'
    Destination = 'C:\Output'
    Force       = $true
    ErrorAction = 'Stop'
}

Copy-Item @params

当 cmdlet 支持 -LiteralPath 时,对具体路径使用该参数。对于仅提供 -Path 的 cmdlet(例如 New-Item),请使用 -Path;如果不确定,请检查 Get-Command <cmdlet> -Syntax。

不要使用 $LASTEXITCODE 来测试 PowerShell cmdlet。请使用终止错误:

$ErrorActionPreference = 'Stop'

将作为参数值传递的表达式用括号括起来,或先赋值:使用 Select-Object -Index (100..120),不要使用 -Index 100..120。

不要直接将语句语法(例如 foreach (...) { ... })的输出通过管道传递;请先赋值语句输出,或使用管道 cmdlet ForEach-Object。

复杂命令

避免使用引号嵌套过多的命令,例如:

cmd.exe /c pwsh.exe -Command "..."

对于多行代码、嵌套引号、JSON、XML、正则表达式、管道、重定向或非 ASCII 路径:

  1. 编写一个临时 .ps1 文件。
  2. 使用以下命令执行:
pwsh.exe -NoLogo -NoProfile -NonInteractive -File script.ps1

除非只是简短、简单的表达式,否则优先使用 -File 而非 -Command。

除非执行策略确实阻止了受信任脚本的运行,否则不要添加 -ExecutionPolicy Bypass。

如果必须从外层 PowerShell 进程通过 -Command 传递脚本,请记住外层 Shell 会先展开 $variables。如果内层脚本包含 $p、$env:...、$PSVersionTable 等内容,请使用外层单引号脚本字符串:

pwsh.exe -NoLogo -NoProfile -Command '$p = "C:\Data Folder\input.txt"; Test-Path -LiteralPath $p'

如果命令必须经过多个解释器或包装器,请停下来改为编写 .ps1 文件,不要继续叠加引号。

字符串和多行代码

  • 对字面字符串和路径使用单引号。
  • 仅在需要 PowerShell 展开时使用双引号。
  • 避免使用反引号进行续行;请使用数组、哈希表、展开传参、括号或脚本块。
  • 不要使用 Bash 风格的 here-doc(例如 python - <<'PY');PowerShell 对 < 的解析方式不同。请使用临时脚本文件,或将 PowerShell here-string 通过管道传递给程序。
  • 对于 JSON,请创建对象并使用 ConvertTo-Json;不要手动转义 JSON。
  • 对于字面多行文本,请使用单引号 here-string:开头的 @' 后不要添加任何字符,并在行首单独使用 '@ 作为结束标记。

编码

PowerShell 7 默认以不带 BOM 的 UTF-8 输出文本;Windows PowerShell 5.1 的默认值因 cmdlet 而异。

  • 创建供其他工具读取的新文本文件时,请明确指定所需编码(通常为 utf8)。
  • 更改现有文本文件前,请确定并保留其现有编码。不要悄悄转换 GBK、UTF-16 或对 BOM 敏感的文件;如果编码不明确,请检查或询问。
  • 默认不要设置 Console.InputEncoding 或 Console.OutputEncoding。只有确认终端或原生程序存在编码不匹配时才设置;$OutputEncoding 则控制 PowerShell 发送给原生程序的文本。
  • 不要对二进制文件应用文本编码选项。请使用字节 API 处理二进制数据。

Start-Process

对于普通的前台执行,请使用:

& $exe @argList

仅在需要提升权限、打开新窗口或隐藏窗口、分离启动或使用 shell 行为时,才使用 Start-Process。

Start-Process -ArgumentList 会将各个值拼接成命令行字符串,并非可靠的结构化参数 API。当参数边界必须精确时,优先使用 ProcessStartInfo.ArgumentList。

需要启动独立进程且参数较复杂时,请使用:

$psi = [System.Diagnostics.ProcessStartInfo]::new()
$psi.FileName = $exe
$psi.UseShellExecute = $false

foreach ($arg in $argList) {
    $psi.ArgumentList.Add($arg)
}

$process = [System.Diagnostics.Process]::Start($psi)
$process.WaitForExit()

if ($process.ExitCode -ne 0) {
    throw "Process failed with exit code $($process.ExitCode)"
}

文件操作

递归删除、移动或覆盖之前:

  • 解析根路径和目标路径的绝对路径。
  • 确认目标位于预期根目录内。
  • 拒绝空路径、根级路径或非预期路径。
  • 在 PowerShell 中执行文件系统更改,不要将路径传递给其他 shell。

映射的驱动器按用户/会话隔离。如果在自动化或沙盒账户下运行 Test-Path X:\... 失败,但交互运行时成功,请使用 whoami 检查当前身份,并查看 Get-PSDrive。如果映射不可见,请向用户索取 UNC 路径、为同一账户建立映射,或在平台支持时将任务切换为当前用户/完全访问执行模式。

版本和模块差异

PowerShell 5.1 和 PowerShell 7 对同一命令可能提供不同的参数或命令类型。依赖特定版本的参数之前,请在当前 shell 中验证语法:

$PSVersionTable.PSVersion
Get-Command Format-Hex -Syntax
Get-Command Get-FileHash -Syntax

例如,PowerShell 7 提供 Format-Hex -Count,而 Windows PowerShell 5.1 不支持。在 5.1 中,请改用管道限制:

Format-Hex -LiteralPath 'C:\Data\buffer.bin' | Select-Object -First 2

如果命令发现行为异常,请先检查 $env:PSModulePath 和 Get-Module -ListAvailable <ModuleName>,再判断 cmdlet 是否缺失。

决策顺序

选择最简单且安全的选项:

  1. PowerShell cmdlet。
  2. & $exe @argList。
  3. 使用 pwsh.exe -File 执行临时 .ps1 文件。
  4. ProcessStartInfo.ArgumentList。
  5. 仅在需要其特殊行为时使用 Start-Process。
  6. 仅在需要 cmd 语义时使用 cmd.exe /c。
  7. 仅在严格受控且别无选择时使用 Invoke-Expression。

不常见的情况和完整示例,请参阅 reference.md。

评论

支持 Markdown