【问题标题】:Powershell auto completion escapes special characters AND uses single quotesPowershell 自动完成转义特殊字符并使用单引号
【发布时间】:2020-11-17 14:01:59
【问题描述】:

我编写了一个以路径为参数的 powershell cmdlet。路径可能包含特殊字符,如 [ 和空格。

一位用户抱怨在使用自动完成时脚本无法处理转义路径。这是 powershell 在自动完成这样的路径时产生的结果:

.\myscript.ps1 '.\file`[bla`].dot'

我的问题:

  1. 为什么powershell自动补全使用单引号转义特殊字符?那不是错了吗?它应该做一个或另一个,但不能同时做。
  2. 处理此类参数的正确方法是什么?

【问题讨论】:

  • 可以,但首先我需要了解它。问题是我来自 UNIX 世界,这些东西过去是不费吹灰之力的。
  • 知道了。 FWIW,PowerShell的行为在这里值得怀疑,我会在GitHub上发布一个问题来跟进。
  • Re Unix:主要区别在于,在 PowerShell 中,您不能使用带引号和不带引号来区分通配符(通配符)模式和文字名称;例如,'*.txt'*.txt 作为命令参数的行为相同,并且取决于被调用的命令将其视为模式还是文本 - 因此不同的 -Path (模式)和 -LiteralPath文件处理 cmdlet 上的(字面量)参数。

标签: powershell


【解决方案1】:

为什么powershell自动补全使用单引号并转义特殊字符?

因为 `-escaping 不是 string-interpolation 转义(仅适用于 "..." 字符串),它是 wildcard-pattern 元字符的转义,恰好使用相同转义字符作为字符串插值转义,`.
换句话说:按照设计,` 字符。 保留作为逐字的'...'字符串的一部分,稍后在通配符模式处理期间进行解释。
请参阅下面的“背景信息”部分,其中讨论了 PowerShell 的选项卡补全何时假定文件名参数不仅仅是文字名称而是通配符模式,就像这里的情况一样。

处理此类参数的正确方法是什么?

防止转义,请确保脚本的(第一个位置)参数命名为
-LiteralPath(参数变量名$LiteralPath

一个函数的快速演示:

# Tab-completing an argument that binds to a -LiteralPath parameter deactivates
# escaping.
function foo { param($LiteralPath) $LiteralPath }; foo .\file[bla].dot

这个要求很不幸,因为名称 -LiteralPath 并不总是合适的 - 不幸的是,将不同的名称与 [Alias('LiteralPath') 属性结合使用不起作用

如果您需要使用不同的参数名称,您可以通过@滚动自己的未转义文件名选项卡完成 987654323@属性:

function foo {
 param(
  [ArgumentCompleter({ 
      param($unused1, $unused2, $wordToComplete)
      (Get-Item ('{0}*' -f ($wordToComplete -replace '[][]', '``$&'))).Name
  })]
  $SomeParam
 ) 
 $SomeParam  # echo for diagnostic purposes
}
foo .\file[bla].dot

注意:

  • 这种简单的实现只适用于当前目录中的文件,但可以适应所有路径。

  • 注意在替换字符串中逐字使用``,这是GitHub issue #7999 中描述的错误的解决方法。


替代方法尝试修复制表符补全行为,而是unescape收到的参数 ,即使参数 没有 转义,它也应该起作用:

function foo { param($SomeParam) [WildcardPattern]::Unescape($SomeParam) }
# The escaped path is now unescaped inside the function.
foo '.\file`[bla`].dot'

注意:万一您的文件名包含 literal `[`] 序列并且用户键入或粘贴这样的字面上的名字,该方法会发生故障。事实上,从 v7.1.0 开始,在这种情况下,即使是带有转义的制表符完成也会失败;例如逐字的`[ 变成转义的``[,即使它应该是```[


警告

  • 如果您确实使用包含[] 的文件路径未转义,则必须确保使用-LiteralPath 参数 稍后将这些路径传递给文件处理 cmdlet,例如 Get-ItemGet-ChildItemGet-Content

背景信息

对于除-LiteralPath 以外的任何参数名称,尤其是-PathPowerShell 假定该参数旨在接受wildcard expressions,因此转义通配符元字符以` 出现在文件名中逐字;因为*? 不能是文件名的一部分(至少在Windows 上),这实际上适用于[]

注意` 在这种情况下不是 字符串插值 转义字符:假设逐字单个引用的字符串( '...') 用于补全,` 字符被保留。但是,如果您在需要 通配符表达式 的上下文中使用这样的字符串,那么通配符引擎会将 ` 识别为 its 转义字符。

相反,这意味着如果您使用expandable(插值)-引号字符串("..."),则必须使用`` 转义通配符元字符 - 和制表符补全如果你以"开始你的论点,确实会这样做


从 v7.1 开始,PowerShell 与文件名相关的制表符补全行为有点令人遗憾:

对于其类型未隐含完成行为的任何参数(例如,[enum] 派生类型)或没有自定义完成的任何参数执行 file-name 完成是合理的默认行为指定逻辑(例如通过[ArgumentCompleterAttribute()][ValidateSet()] 属性)。

但是:

  • 除了看似硬编码的-LiteralPath 异常之外,假设所有这些参数默认支持通配符表达式是否有意义值得怀疑。 p>

    • 理想情况下,通配符支持应仅从目标参数上是否存在[SupportsWildcards()] 来推断 - 请参阅GitHub suggestion #14149

    • 推测默认支持通配符的原因是文件处理cmdlet的第一个位置参数是基于通配符的-Path参数(例如Get-ChildItem *.txt),为此需要逐字转义 []

      • 虽然转义使得将参数传递给此类 cmdlet位置 变得更加容易,但这也意味着如果参数不是通过制表符完成获得的,例如通过键入或粘贴名称 - 也必须执行转义,这可能是意料之外的。

      • 相反,需要 非转义 路径并将-LiteralPath 与文件处理 cmdlet 一起使用的代码会因制表符完成而导致转义路径中断。

  • type-constrained 参数上执行文件名补全是没有意义的 @ / [System.IO.DirectoryInfo](或其数组)。

    • 例如,function foo { param([int] $i) } 目前出乎意料地也会用制表符补全文件名,尽管这没有任何意义(如果尝试调用,则会中断调用)。

【讨论】:

    【解决方案2】:

    看起来它已在 powershell 7 中修复。在 powershell 5 中,您必须使用 -literalpath。

    dir '.\file`[1`].txt'
    
        Directory: C:\Users\js\foo
    
    Mode                 LastWriteTime         Length Name
    ----                 -------------         ------ ----
    -a---          11/17/2020  9:04 AM              5 file[1].txt
    

    【讨论】:

    • 它似乎对内置 cmdlet 有效,但我说的是我自己的 cmdlet。
    • 那是我的错误:我天真地认为Get-ItemGet-ChildItem 的行为相同,但事实并非如此,因此带有转义路径的Get-ChildItem 在Windows PowerShell (v5) 中确实仍然存在问题.1),并且可能不会得到修复(但是,正如您所说,它已在 PowerShell [Core] 中修复)。在 Windows PowerShell 中,您必须将反引号 加倍 以使其与 Get-ChildItem 一起使用,这个问题仍然会影响 other 区域中的 PowerShell [Core] - 请参阅 @ 987654321@
    • 也就是说,这个问题是手头问题的附带问题:Richard 希望文件名以制表符完成未转义,以绑定到自定义脚本的参数。
    猜你喜欢
    • 2017-02-08
    • 2020-02-11
    • 1970-01-01
    • 1970-01-01
    • 2011-09-23
    • 2018-12-21
    • 1970-01-01
    • 2014-08-23
    • 2015-02-01
    相关资源
    最近更新 更多