以下将数组中的所有字符串包含在嵌入的"..." 中,并另外转义预先存在的 嵌入的" 字符。 \",这是外部程序通常期望的:
注意:在 Windows PowerShell 和 PowerShell (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。
Preview 版本的 v7.2 现在附带 experimental featurePSNativeCommandArgumentPassing,这是一个尝试修复,但不幸的是,看起来它对于 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) 带有一个向后和向前兼容的辅助函数 ie,这也避免了额外转义的需要,同时还包含实验性功能中缺少的 Windows 上高配置 CLI 的重要调整:
# 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 解析它的命令行时,它最终会看到以下逐字字符串作为最后一个参数:
/CommandParameters="a" "b c" "d\"e"
在 Bash 中,我可以使用 printf %q 正确转义参数。
不幸的是,PowerShell没有等效功能,但临时提供它并不难:
Powershell 中的元引用字符串:
注意:
-
meta-quoting 我的意思是 Bash 的 printf %q 提供的功能:它将给定的字符串值格式化为可用作 source 中的 字符串文字代码。比如(这个例子说明了一般的原理,不是printf %q的实际行为),逐字字符串值a b被转换成逐字字符串值"a b",后者可以在构造命令行时作为参数存储在字符串中。
-
所需的方法取决于元引用字符串是用于 PowerShell 命令的字符串表示(例如 cmdlet 调用)还是用于调用 外部程序,考虑到他们不同的转义需求。此外,虽然 Windows 上的大多数外部程序(也)将\" 理解为转义的",但有些——尤其是批处理文件和msiexec.exe——只理解""。
以下命令使用以下示例输入输入字符串,其中包含' 和"(为了引用方便,逐字逐句构造here-string):
$str = @'
6'1" tall
'@
以下解决方案使用 -f operator 来合成结果字符串,这不仅是为了概念清晰,也是为了解决字符串插值错误 这可能导致嵌入在expandable strings 中的子表达式产生不正确的结果(例如,"a$('""')b" 和"a$('`"')b" 都逐字产生a"b - 一个" / ` 缺失);另一种方法是使用简单的字符串 concatenation 和 +。
结果字符串的逐字内容显示在源代码 cmets 中,包含在 «...» 中;例如«"6'1\""»(此表示仅用于说明;PowerShell 确实不支持此类分隔符)。
元引用外部程序:
注意:
-
在 Windows 上,控制台应用程序通常仅将 双 引号识别为命令行中的字符串分隔符,因此以下解决方案重点关注这一点。 要创建一个 单 引用的表示,它可以被 POSIX 兼容的 shell(例如 bash)理解,请使用以下命令:
"'{0}'" -f ($str -replace "'", "'\''"),它会产生逐字的 @ 987654386@(原文如此)。
-
在 Windows 的边缘情况下,您可能必须完全绕过 PowerShell 的命令行解析,以便完全控制用于在幕后创建实际进程的命令行,或者通过 @ 987654387@,stop-parsing symbol,它有严格的限制,或者通过将调用委托给cmd.exe,将整个命令行传递给/c - 再次,请参阅this answer。
对于需要\"-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 的外部程序(例如,批处理文件,msiexec - 仅限 Windows):
# 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""»
元引用PowerShell 命令:
注意:
- 幸运的是,在这种情况下,不需要 额外 转义;上面讨论的参数传递错误只影响对外部程序的调用。
创建一个 双引号 表示 ("..."):
'"{0}"' -f ($str -replace '"', '`"') # -> «"6'1`" tall"»
创建一个单引用的表示 ('...'):
"'{0}'" -f ($str -replace "'", "''") # -> «'6''1" tall'»