如何处理带有特殊字符的文件路径?

How to handle file paths with special characters?

我有一个 PowerShell 脚本,它需要能够从同一目录中使用提升的凭据调用另一个 PS 脚本。由于脚本需要可移植,因此无法提前知道脚本的文件路径,并且需要能够在不失败的情况下处理文件路径中的空格和特殊字符。

由于提升 PS 会话会导致当前工作目录更改为 "C:\Windows\System32",因此我需要能够将当前目录作为参数传递给脚本。现在,只要目录路径中没有特殊字符,我当前的脚本(如下)就可以很好地处理这个问题。

这是我当前的脚本(根据@mklement0 的建议更新):

$scriptpath = $MyInvocation.MyCommand.Path
$dir = Split-Path $scriptpath

Start-Process Powershell -Verb runas -ArgumentList "-ExecutionPolicy","Bypass","-File","`"$dir\elevated.ps1`"","`"$dir`""

我试过转义一些特殊字符,例如:

$dir = $dir.Replace('&','`"&`"')

但是对于 Windows 在 folder/filename 中允许的所有不同字符,这并不是一致的。也不是所有的特殊字符都需要转义,但我需要确保这对那些需要转义的字符有效;至少那些可能出现在文件路径中的。

我主要关心的特殊字符(尽管有时也需要解决外来字符):!@#$%^_+-=.,{}`~&()[]

我正在测试的示例目录路径:

C:\Users\Administrator\Desktop\Test Folder\badname-!@#$%^_+-=.,{}`~&()[]

尝试从上述位置 运行 我的脚本 returns 出现以下错误:

The specified wildcard character pattern is not valid:
badname-!@#$%^_+-=.,{}\~&()[]
    + CategoryInfo          : NotSpecified: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : RuntimeException

那么,让脚本动态处理目录路径中的特殊字符的最佳方法是什么?

顺便说一句:在 PSv3+ 中,您可以使用 $PSScriptRoot 来指代脚本所在的目录。
如果你可以修改目标脚本,你可以将Set-Location -LiteralPath $PSScriptRoot放在顶部以更改为该文件夹。


为避免 quoting/escaping 问题,将参数 单独 作为 数组 的不同元素传递给 -ArgumentList (-Args):

Start-Process powershell -Verb runas -ArgumentList '-ExecutionPolicy', 'Bypass', 
  '-File', "`"$dir\elevated.ps1`"", "`"$dir`""

不幸的是,仅此一项不够:

  • 当你使用-File时,即使参数单独传递,脚本文件路径和目录路径仍然需要embedded "..." 引用 以便正确传递带有嵌入空格的值: "`"..."`" 传递带有 embedded 双引号的值 (` 是 PowerShell 的转义字符,因此 `" 转义双引号)。 提示 Ansgar Wiechers
    这个bug has been reported on GitHub.

    • 如果您需要传递带有嵌入式 " 字符的参数(根据定义,文件系统路径从不包含),请使用类似:
      ('"{0}"' -f ($value -replace '"', '\"'))
  • 遗憾的是,如果您的路径包含 [ 个字符,而这些字符并不是语法上有效的通配符模式范围的每个部分(例如,[ab], [0-9]), 因为 PowerShell - 自 Windows PowerShell v5.1 / PowerShell Core v6.0.0-beta.6 - 错误地将路径解释为 通配符模式,如果发现格式不正确则失败 - 请参阅 this GitHub issue

    • 换句话说:您的调用 可能会或可能不会 工作,这取决于您的路径中如何使用 [ 并且可能 ];例如foo[1] 有效,但 foo[] 无效。
    • 目前没有适用于所有 情况的解决方法。

替代-Command

-Commmand 允许您传递一段 PowerShell 代码 - 一个迷你脚本,如果您愿意的话 - 您可以使用它来 传递一个由 PowerShell 解析的带引号的字符串新实例的规则

Start-Process powershell -Verb runas -ArgumentList '-ExecutionPolicy', 'Bypass', 
  '-Command', "& `"$dir\elevated.ps1`" `"$dir`""

请注意需要使用 & 来调用脚本,因为它是作为带引号的字符串给出的,并且 $dir 的扩展是由 current届会。

不幸的是,文件名恰好看起来像格式错误的通配符模式(上面讨论过)的问题也适用于此调用场景。

因此,只要括号不为空 [].

,我就能找到一个可行的解决方案

这是我得到的:

Start-Process Powershell -Verb runas -ArgumentList "-ExecutionPolicy","Bypass","-File","`"$(Get-Item -LiteralPath $PSScriptRoot\elevated.ps1)`""

以此为测试路径:

C:\Users\Administrator\Desktop\Test Folder\badname-!@#$%^_+-=.,{}`~[&]()

带有空括号的路径 [] 仍然不起作用,但 return 不会出错。我假设这意味着它无法在预期路径中找到文件,同时仍将括号解释为通配符。不确定是否真的有什么可以解决这个问题,除了可能检查 运行 之前的路径并抛出错误。