如何转义字符串以将其传递给另一个命令?
How to escape a string to pass it to another command?
我有一组参数$p = "a", "b c", "d`"e"
要传递给命令。
我可以这样做:
& my_command $p
现在,我需要在一些外部包装器(二进制)中调用相同的命令,它使用固定参数像这样工作:
& my_wrapper /Command=my_command "/CommandParameters=a 'b c' 'd`"e'"
但是如何从数组 $p
传递 /CommandParameters
?
一种适用于某些输入的方法是像这样预处理数组:
$p = $p | Foreach {"\`"$_\`""}
& my_wrapper /Command=my_command "/CommandParameters=$p"
但这似乎很脆弱。 $p
中的值包含空格和引号。
在 Bash 中,我可以使用 printf %q
来正确转义参数。
以下 将数组中的所有字符串包含在嵌入的 "..."
中,并另外转义 预先存在的 嵌入的 "
字符。作为 \"
,这是外部程序通常期望的:
注意:在 Windows PowerShell 和 PowerShell(核心)中,至少 v7.1,附加轮\
转义是 - 不幸的是 - 在将带有嵌入式"
的参数传递给外部程序[=211时需要=]:
$p = "a", "b c", "d`"e"
$pEscaped = $p.ForEach({ '\"{0}\"' -f ($_ -replace '"', '\\"') })
& my_wrapper /Command=my_command "/CommandParameters=$pEscaped"
注意:虽然 $pEscaped
是字符串的 集合 (数组),但在 expandable string("..."
) 自动创建一个 space 分隔的单行表示;例如"$('foo', 'bar')"
逐字输出 foo bar
这个长期存在的问题 - 意外需要手动 \
- 转义嵌入的 "
字符。向 外部程序 传递参数时 - 在 this answer.
中进行了总结
预览 v7.2 版本现在带有 experimental feature PSNativeCommandArgumentPassing
, which is an attempted fix, but, unfortunately, it looks like it will lack important accommodations for high-profile CLIs on Windows - see this summary from 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 '"', '\"') }))
注:
- 假设此实验性功能成为官方功能(通常无法保证),更正后的行为很可能是选择加入[=211] =],通过偏好变量:
$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 '"', '\"') }))
至于你试过的:
[Enclosing the value in embedded "
] seems pretty fragile
如果你另外将任何预先存在的"
转义为\"
(\\"
,如果必须补偿参数传递错误) ,如上所示,它运行稳健。
最终,必须执行以下命令行,PowerShell 在幕后构造:
my_wrapper /Command=my_command "/CommandParameters=\"a\" \"b c\" \"d\\"e\""
当 my_wrapper
解析其命令行时,它最终看到以下逐字字符串作为最后一个参数:
/CommandParameters="a" "b c" "d\"e"
In Bash, I could use printf %q
to properly escape the parameters.
不幸的是,PowerShell 没有 等效功能,但不难临时提供它:
Powershell 中的元引用字符串:
注:
meta-quoting 我的意思是 Bash 的 printf %q
提供的功能:它将给定的字符串值格式化为在源代码中可用作字符串文字。例如(这个例子说明了一般原则,而不是 printf %q
的实际行为),逐字字符串值 a b
被转换为逐字字符串值 "a b"
,后者可以用作构造存储在字符串中的命令行时的参数。
所需的方法取决于是否要在 PowerShell 命令(例如 cmdlet 调用)的字符串表示中使用元引号字符串或调用 外部程序 ,因为它们有不同的转义需求。此外,虽然 Windows 上的大多数外部程序(也)将 \"
理解为转义的 "
,但一些 - 特别是批处理文件和 msiexec.exe
- 只理解 ""
.
下面的命令使用以下示例输入字符串,其中包含 '
和 "
(为了引用方便,通过逐字 here-string 构造):
$str = @'
6'1" tall
'@
下面的解决方案使用 -f
operator to synthesize the result strings, not just for conceptual clarity, but also to work around string interpolation bugs that can cause subexpressions embedded in expandable strings 来产生不正确的结果(例如,"a$('""')b"
和 "a$('`"')b"
都逐字产生 a"b
- 一个 "
/ `
缺失);另一种方法是使用简单的字符串 concatenation 和 +
.
- 不幸的是,看起来这些错误不会被修复,以保持向后兼容性;见 GitHub issue #3039
结果字符串的逐字内容显示在源代码注释中,包含在 «...»
中;例如«"6'1\""»
(此表示仅供说明;PowerShell 不 支持此类分隔符)。
元引用外部程序:
注:
在 Windows 上,控制台应用程序通常只在其命令中将 双 引号识别为字符串定界符线,所以这就是下面的解决方案所关注的。 要创建一个 单引号 引号表示,可以被 POSIX 兼容的 shell(例如 bash
理解,请使用以下命令:
"'{0}'" -f ($str -replace "'", "'\''")
,逐字生成 '6'\''1" tall'
(原文如此)。
在Windows的边缘情况下,您可能不得不绕过PowerShell的命令行解析,从而完全控制命令行用于在幕后创建实际进程,通过 --%
、具有严格限制的 停止解析符号 或将调用委托给 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-only):
# 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 命令:
注:
- 幸运的是,在这种情况下不需要额外的转义;上面讨论的参数传递错误只影响对外部程序.
的调用
创建一个 double-quoted 表示 ("..."
):
'"{0}"' -f ($str -replace '"', '`"') # -> «"6'1`" tall"»
创建单-引号表示('...'
):
"'{0}'" -f ($str -replace "'", "''") # -> «'6''1" tall'»
我有一组参数$p = "a", "b c", "d`"e"
要传递给命令。
我可以这样做:
& my_command $p
现在,我需要在一些外部包装器(二进制)中调用相同的命令,它使用固定参数像这样工作:
& my_wrapper /Command=my_command "/CommandParameters=a 'b c' 'd`"e'"
但是如何从数组 $p
传递 /CommandParameters
?
一种适用于某些输入的方法是像这样预处理数组:
$p = $p | Foreach {"\`"$_\`""}
& my_wrapper /Command=my_command "/CommandParameters=$p"
但这似乎很脆弱。 $p
中的值包含空格和引号。
在 Bash 中,我可以使用 printf %q
来正确转义参数。
以下 将数组中的所有字符串包含在嵌入的 "..."
中,并另外转义 预先存在的 嵌入的 "
字符。作为 \"
,这是外部程序通常期望的:
注意:在 Windows PowerShell 和 PowerShell(核心)中,至少 v7.1,附加轮\
转义是 - 不幸的是 - 在将带有嵌入式"
的参数传递给外部程序[=211时需要=]:
$p = "a", "b c", "d`"e"
$pEscaped = $p.ForEach({ '\"{0}\"' -f ($_ -replace '"', '\\"') })
& my_wrapper /Command=my_command "/CommandParameters=$pEscaped"
注意:虽然 $pEscaped
是字符串的 集合 (数组),但在 expandable string("..."
) 自动创建一个 space 分隔的单行表示;例如"$('foo', 'bar')"
逐字输出 foo bar
这个长期存在的问题 - 意外需要手动 \
- 转义嵌入的 "
字符。向 外部程序 传递参数时 - 在 this answer.
预览 v7.2 版本现在带有 experimental feature PSNativeCommandArgumentPassing
, which is an attempted fix, but, unfortunately, it looks like it will lack important accommodations for high-profile CLIs on Windows - see this summary from 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 '"', '\"') }))
注:
- 假设此实验性功能成为官方功能(通常无法保证),更正后的行为很可能是选择加入[=211] =],通过偏好变量:
$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 '"', '\"') }))
至于你试过的:
[Enclosing the value in embedded
"
] seems pretty fragile
如果你另外将任何预先存在的"
转义为\"
(\\"
,如果必须补偿参数传递错误) ,如上所示,它运行稳健。
最终,必须执行以下命令行,PowerShell 在幕后构造:
my_wrapper /Command=my_command "/CommandParameters=\"a\" \"b c\" \"d\\"e\""
当 my_wrapper
解析其命令行时,它最终看到以下逐字字符串作为最后一个参数:
/CommandParameters="a" "b c" "d\"e"
In Bash, I could use
printf %q
to properly escape the parameters.
不幸的是,PowerShell 没有 等效功能,但不难临时提供它:
Powershell 中的元引用字符串:
注:
meta-quoting 我的意思是 Bash 的
printf %q
提供的功能:它将给定的字符串值格式化为在源代码中可用作字符串文字。例如(这个例子说明了一般原则,而不是printf %q
的实际行为),逐字字符串值a b
被转换为逐字字符串值"a b"
,后者可以用作构造存储在字符串中的命令行时的参数。所需的方法取决于是否要在 PowerShell 命令(例如 cmdlet 调用)的字符串表示中使用元引号字符串或调用 外部程序 ,因为它们有不同的转义需求。此外,虽然 Windows 上的大多数外部程序(也)将
\"
理解为转义的"
,但一些 - 特别是批处理文件和msiexec.exe
- 只理解""
.
下面的命令使用以下示例输入字符串,其中包含 '
和 "
(为了引用方便,通过逐字 here-string 构造):
$str = @'
6'1" tall
'@
下面的解决方案使用 -f
operator to synthesize the result strings, not just for conceptual clarity, but also to work around string interpolation bugs that can cause subexpressions embedded in expandable strings 来产生不正确的结果(例如,"a$('""')b"
和 "a$('`"')b"
都逐字产生 a"b
- 一个 "
/ `
缺失);另一种方法是使用简单的字符串 concatenation 和 +
.
- 不幸的是,看起来这些错误不会被修复,以保持向后兼容性;见 GitHub issue #3039
结果字符串的逐字内容显示在源代码注释中,包含在 «...»
中;例如«"6'1\""»
(此表示仅供说明;PowerShell 不 支持此类分隔符)。
元引用外部程序:
注:
在 Windows 上,控制台应用程序通常只在其命令中将 双 引号识别为字符串定界符线,所以这就是下面的解决方案所关注的。 要创建一个 单引号 引号表示,可以被 POSIX 兼容的 shell(例如
bash
理解,请使用以下命令:
"'{0}'" -f ($str -replace "'", "'\''")
,逐字生成'6'\''1" tall'
(原文如此)。在Windows的边缘情况下,您可能不得不绕过PowerShell的命令行解析,从而完全控制命令行用于在幕后创建实际进程,通过
--%
、具有严格限制的 停止解析符号 或将调用委托给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-only):
# 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 命令:
注:
- 幸运的是,在这种情况下不需要额外的转义;上面讨论的参数传递错误只影响对外部程序. 的调用
创建一个 double-quoted 表示 ("..."
):
'"{0}"' -f ($str -replace '"', '`"') # -> «"6'1`" tall"»
创建单-引号表示('...'
):
"'{0}'" -f ($str -replace "'", "''") # -> «'6''1" tall'»