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

3
我有一个 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('&','`"&`"')

但是这种方法并不能保证在所有允许使用的文件夹/文件名字符中都能正常工作。并非所有特殊字符都需要转义,但我需要确保它适用于那些需要转义的字符; 至少适用于可能出现在文件路径中的字符。
我主要关注以下特殊字符(尽管外国字符也可能需要在某个时候进行处理):!@#$%^_+-=.,{}`~&()[] 我正在测试的示例目录路径为:
C:\Users\Administrator\Desktop\Test Folder\badname-!@#$%^_+-=.,{}`~&()[]
试图从上述位置运行我的脚本会返回以下错误:
指定的通配符字符模式无效:
badname-!@#$%^_+-=.,{}\~&()[]
    + CategoryInfo          : NotSpecified: (:) [], ParentContainsErrorRecordException
    + FullyQualifiedErrorId : RuntimeException
那么,有什么最好的方法使脚本动态处理目录路径中的特殊字符呢?
2个回答

3
顺便说一句,在PSv3+中,你可以使用$PSScriptRoot来引用脚本所在的目录。
如果你可以修改目标脚本,你可以将Set-Location -LiteralPath $PSScriptRoot放在顶部以切换到该文件夹。
为了避免引用/转义问题,将参数作为一个数组的不同元素,逐个传递给-ArgumentList-Args)。
# Update: While this works, it is ultimately simpler to pass a *single string* 
#         with *embedded double-quoting*.
Start-Process powershell -Verb runas -ArgumentList '-ExecutionPolicy', 'Bypass', 
  '-File', "`"$dir\elevated.ps1`"", "`"$dir`""

更新:

  • 虽然下面的解释仍然正确,但如果需要的话,最终更简单的方法是传递一个带有嵌入双引号的单个字符串,而不是将双引号嵌入传递给-ArgumentList的各个参数,如上所示 - 请参阅this answer

不幸的是,仅此还不够:

当你使用-File时,即使参数是逐个传递的,脚本文件路径和目录路径仍然需要用引号嵌套来正确传递带有嵌入空格的值:"..."通过带有嵌入双引号的值(`是PowerShell的转义字符,所以`"转义了双引号)。感谢Ansgar Wiechers的建议。这个错误已经报告为GitHub问题#5576;不幸的是,为了向后兼容性,它不会被修复。
如果您需要传递带有嵌入"字符的参数(文件系统路径从定义上永远不包含),可以使用类似的方法:('"{0}"' -f ($value -replace '"', '\"'))。
遗憾的是,即使这样做也不足够,如果您的路径包含不是语法上有效的通配符模式范围的[字符(例如[ab],[0-9]),因为PowerShell在Windows PowerShell和PowerShell(Core)中(至少到v7.3.6)错误地将路径解释为通配符模式,并且如果发现格式错误,则失败-请参阅GitHub问题#4726。
换句话说:根据路径中如何使用[和可能的],您的调用可能起作用,也可能不起作用;例如foo[1]可以工作,但foo[]不行。
目前没有适用于所有情况的解决方法。

使用-Command参数的替代方法:

-Command允许您传递一段PowerShell代码 - 一个迷你脚本,您可以使用它来传递一个由PowerShell规则解析的单个引用字符串给新实例:

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

请注意需要使用&来调用脚本,因为它作为一个引号字符串给出,并且$dir的扩展由当前会话执行。

不幸的是,文件名看起来像是格式错误的通配符模式(如上所述)的问题也适用于这种调用场景。


0
所以,我能够找到一个解决方案,只要括号不为空就有效[]
下面是我的代码:
Start-Process Powershell -Verb runas -ArgumentList "-ExecutionPolicy","Bypass","-File","`"$(Get-Item -LiteralPath $PSScriptRoot\elevated.ps1)`""
使用此路径作为测试路径:
C:\Users\Administrator\Desktop\Test Folder\badname-!@#$%^_+-=.,{}`~[&]()

一条带有空括号[]的路径仍然无法工作,但不会返回错误。 我假设这意味着在将括号解释为通配符的同时未能找到预期路径中的文件。 不确定是否真的有什么可以修复它,除非在运行之前检查路径并抛出错误。


更一般的说,如果您的路径包含不是语法上有效的通配符模式范围的 [ 字符(例如 [ab][0-9]),则调用将失败,因为 PowerShell 错误地 将该路径解释为一个 通配符模式 并在发现其格式错误时失败。换句话说:(a) 这应该被视为一个 _bug_。(b) 截至 Windows PowerShell v5.1 / PowerShell Core v6.0.0-beta.6,没有普遍健壮的解决方案。 - mklement0
请注意,在 "\"...`""中不需要$(Get-Item -LiteralPath $PSScriptRoot\elevated.ps1)- 只需使用"`"$PSScriptRoot\elevated.ps1`""` 即可。 - mklement0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接