如何转义字符串以将其传递给另一个命令? Powershell 中的元引用字符串:

问题描述

我有一个参数数组 $p = "a","b c","d`"e" 要传递给命令。 我可以这样做:

& my_command $p

现在,我需要在一些外部包装器(二进制)中调用相同的命令,它使用固定参数的工作方式如下:

& my_wrapper /Command=my_command "/CommandParameters=a 'b c' 'd`"e'"

但是如何从数组 /CommandParameters 中传递 $p


一种适用于某些输入的方法是像这样预处理数组:

$p = $p | Foreach {"\`"$_\`""}
& my_wrapper /Command=my_command "/CommandParameters=$p"

但这似乎很脆弱。 $p 中的值确实包含空格和引号。


在 Bash 中,我可以使用 printf %q 来正确地转义参数。

解决方法

以下将数组中的所有字符串包含在嵌入的 "..." 中,并额外转义预先存在的嵌入的 " 字符。如\",这是外部程序通常所期望的:

注意:Windows PowerShellPowerShell (Core) 至少 v7.1 中,额外一轮\-escaping 是 - 不幸的是 - 将带有嵌入式 " 的参数传递给外部程序时需要:

$p = "a","b c","d`"e"
$pEscaped = $p.ForEach({ '\"{0}\"' -f ($_ -replace '"','\\\"') })
& my_wrapper /Command=my_command "/CommandParameters=$pEscaped"

注意:虽然 $pEscaped 是字符串的 集合(数组),但在 expandable string ("...") 中使用它会自动创建一个空格- 分隔的单行表示;例如"$('foo','bar')" 逐字产生 foo bar

这个长期存在的问题 - 意外需要手动 \ 转义嵌入的 " 字符。向外部程序传递参数时 - 在 this answer 中总结。

v7.2 的

预览 版本现在带有 experimental feature PSNativeCommandArgumentPassing,这是一个尝试修复,但是,不幸的是,它似乎缺少 Windows 上引人注目的 CLI 的重要调整 - 请参阅 GitHub issue #15143 中的此摘要。但是,此修复对期望 " 转义为 \" 的可执行文件有效,因此解决方案简化为(使用字符串连接 (+) 在表达式 ((...)) 来构造内嵌参数):

# Note: Requires experimental feature PSNativeCommandArgumentPassing
#       to be turned on,available in preview versions of v7.2
& my_wrapper /Command=my_command ('/CommandParameters=' + $p.ForEach({ '"{0}"' -f ($_ -replace '"','\"') }))

注意:

  • 假设此实验性功能成为官方功能(通常无法保证),更正后的行为很可能是选择加入,通过偏好变量$PSNativeCommandArgumentPassing = 'Standard''Legacy' 选择旧行为)。

如果您不介意安装第三方模块(由我编写),Native module (Install-Module Native) 带有向后和向前兼容的帮助函数,{ {1}},这也避免了额外转义的需要,同时还包含实验性功能中缺少的 Windows 上高调 CLI 的重要调整:

ie

至于你尝试了什么

[将值包含在嵌入的 # Note: Assumes `Install-Module Native` was called. # Just use `ie` instead of `&` ie my_wrapper /Command=my_command ('/CommandParameters=' + $p.ForEach({ '"{0}"' -f ($_ -replace '"','\"') })) ] 中似乎很脆弱

如果您另外将任何预先存在的 " 转义为 "\",如果必须补偿参数传递错误),如上所示,它运行良好。

最终,必须执行以下命令行,这是 PowerShell 在幕后构建的

\\\"

my_wrapper /Command=my_command "/CommandParameters=\"a\" \"b c\" \"d\\\"e\"" 解析其命令行时,它最终将以下逐字字符串视为最后一个参数:

my_wrapper

在 Bash 中,我可以使用 /CommandParameters="a" "b c" "d\"e" 来正确地转义参数。

不幸的是,PowerShell 没有等效功能,但不难提供它:


Powershell 中的元引用字符串:

注意:

  • 元引用我的意思是 Bash 的 printf %q 提供的功能:它格式化给定的字符串值以使其可用作字符串文字源代码中。例如(这个例子说明了一般原理,而不是printf %q的实际行为),逐字字符串值printf %q被转换为逐字字符串值a b,后者可以用作构造存储在字符串中的命令行时的参数。

  • 所需的方法取决于元引用的字符串是在 PowerShell 命令(例如 cmdlet 调用)的字符串表示中使用,还是在调用外部程序,鉴于他们不同的逃逸需求。此外,虽然 Windows 上的大多数外部程序(也)将 "a b" 理解为转义的 \",但某些 - 特别是批处理文件和 " - 仅理解 msiexec.exe

以下命令使用以下示例输入输入字符串,其中包含 ""'(为了引用方便,通过逐字 here-string 构造):

"

以下解决方案使用 -f operator 来合成结果字符串,不仅是为了概念清晰,而且是为了解决字符串插值错误 可能导致嵌入在 expandable strings 中的子表达式产生不正确的结果(例如,$str = @' 6'1" tall '@ "a$('""')b" 都产生逐字 "a$('`"')b" - 一个 a"b / " 缺失);另一种方法是使用带有 ` 的简单字符串连接

  • 不幸的是,看起来这些错误不会被修复,以保持向后兼容性;见GitHub issue #3039

结果字符串的逐字内容显示在源代码注释中,括在 + 中;例如«...»(此表示仅用于说明;PowerShell 支持此类分隔符)。

元引用外部程序

注意:

  • 在 Windows 上,控制台应用程序通常只在其命令行中将双引号识别为字符串分隔符,因此这就是以下解决方案的重点。 要创建一个引用的表示,它可以被 POSIX 兼容的 shell(例如 «"6'1\""»)理解,请使用以下内容:
    bash,产生逐字 "'{0}'" -f ($str -replace "'","'\''")(原文如此)。

  • 在 Windows 的边缘情况下,您可能必须绕过 PowerShell 的命令行解析,以便完全控制用于幕后实际进程创建的命令行,通过 '6'\''1" tall'stop-parsing symbol,它有严格的限制,或者通过将调用委托给 --%,通过将整个命令行传递给 cmd.exe - 再次,参见 {{3 }}。

对于需要/c-escaping(典型)的外部程序:

\"

对于需要 # With the experimental,pre-v7.2 PSNativeCommandArgumentPassing # feature turned on,you can directly pass the result # as an external-program argument. # Ditto with the `ie` helper function. '"{0}"' -f ($str -replace '"','\"') # -> «"6'1\" tall"» # With additional \-escaping to compensate for PowerShell's # argument-passing bug,required up to at least v7.1 '\"{0}\"' -f ($str -replace '"','\\\"') # -> «\"6'1\\\" tall\"» -escaping 的外部程序(例如,批处理文件,"" - 仅限 Windows):

msiexec

元引用用于 PowerShell 命令

注意:

  • 幸运的是,在这种情况下不需要额外转义;上面讨论的参数传递错误只影响对外部程序的调用。

创建双引号表示(# CAVEAT: With the experimental,pre-v7.2 PSNativeCommandArgumentPassing # feature turned on,passing the result as an external-program argument # will NOT work as intended,because \" rather than "" is invariably used. # By contrast,the `ie` helper function automatically # switches to "" for batch files,msiexec.exe and msdeploy.exe # and WSH scripts. '"{0}"' -f ($str -replace '"','""') # -> «"6'1"" tall"» # With additional escaping to compensate for PowerShell's # argument-passing bug,required up to at least v7.1 '""{0}""' -f ($str -replace '"','""""') # -> «""6'1"""" tall""»

"..."

创建一个引用表示('"{0}"' -f ($str -replace '"','`"') # -> «"6'1`" tall"»

'...'

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...