根据我的理解,invoke运算符(&)和Invoke-Expression cmdlet的行为应该相似。但是,如下所示,情况并非如此:
PS C:\Users\admin> powershell -Command "& {""([Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('ZWNobyAnaGVsb
G93b3JsZCc=')))""}"
echo 'helloworld'
PS C:\Users\admin> powershell -Command "IEX ""([Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('ZWNobyAnaGVs
bG93b3JsZCc=')))"""
helloworld
此处,'ZWNobyAnaGVsbG93b3JsZCc='
是Base64编码的字符串"echo helloworld"
。
有人可以澄清吗?
答案 0 :(得分:6)
Invoke-Expression
(其内置别名为iex
)和&
, the call operator用于不同目的:
Invoke-Expression
将给定字符串评估为PowerShell源代码 ,就好像您已执行字符串内容< / em>直接作为命令。
因此,它类似于eval
中的bash
,因此仅用于完全在调用者控件下的输入或输入来电者信任。
&
用于调用命令(& <nameOrPath> [...]
)或脚本块({{1} })强>:
在手头的情况下:
您的命令的核心是以下表达式,它返回字符串
& { ... } [...]
(其内容不包含封闭的"echo 'helloworld'"
- 这只是结果字符串作为PowerShell字符串文字的表示形式):
"
另请注意,由于解析命令行的方式,原始命令中核心表达式周围的[Text.Encoding]::UTF8.GetString([Convert]::FromBase64String('ZWNobyAnaGVsbG93b3JsZCc='))
实际上是忽略,这解释了为什么表达式为执行而不是被视为字符串的内容。 [1]
因此,您的两个命令相当于:
""...""
& { "echo 'helloworld'" }
执行脚本块中的语句,该语句恰好是字符串,并且字符串本身 - 如果它没有分配给变量或重定向到其他地方 - 按原样只是输出
在这种情况下,该命令实际上与仅执行&
一样(包括封闭的"echo 'helloworld'"
,您可以将其视为
"
),因此echo "echo 'helloworld'"
会打印到控制台。
请注意,echo 'helloworld'
是echo
cmdlet的内置别名,很少需要使用显式:命令或表达式的返回值为< em>隐式输出,如果它们没有以某种形式捕获,就像在这种情况下,将字符串本身作为语句执行只是输出字符串。 (例如,您可以在提示时提交Write-Output
来尝试此操作。
'hi'
iex "echo 'helloworld'"
(iex
)将字符串的内容评估为源代码,因此执行Invoke-Expression
,将echo 'helloworld'
打印到控制台。< / LI>
注意:
据我所知,处理关于外部程序或从外部程序调用的引用不是官方文档的一部分(截至撰写本文时,about_Parsing和{{ 3}}或about_Quoting_Rules提到它 - 我已经打开about_Special_Characters来解决这个问题。
现有处理存在缺陷,但在不破坏向后兼容性的情况下无法修复。
从PowerShell调用时,最好的方法是使用脚本块,绕过引用问题 - 见下文。
即使您通过从PowerShell- 内部角度将整个helloworld
字符串中的"
转义为""
来正确嵌入"..."
, 需要"
\
的额外转义才能将其传递到外部程序 ,即使该外部程序是另一个通过powershell.exe
调用PowerShell的实例。
采用这个简化的例子:
powershell.exe -command " ""hi"" " # !! BROKEN
powershell.exe -command ' "hi" ' # !! BROKEN
PowerShell内部," ""hi"" "
和' "hi" '
评估为包含文字内容 "hi"
的字符串,执行后会打印hi
。
令人遗憾的是,PowerShell将此字符串作为powershell.exe
传递给" "hi" "
- 请注意""
如何变为普通"
以及封闭的单} 引号被替换为 double 引号 - 在新实例解析后有效地导致 hi
(因为" "hi" "
被解析为连接子串" "
,hi
和" "
),因此PowerShell最终会尝试执行一个名为hi
的命令命令。< / p>
相比之下,如果您设法将嵌入式"
作为\"
(原文如此) - 在满足PowerShell自身的转义需求之后传递 - 该命令可以正常工作如预期。
因此,如上所述,您需要将组合 PowerShell内部转义与for-for-CLI转义相结合,以便传递嵌入式"
,以便:< / p>
"..."
内,每个嵌入式"
必须转发为\""
(原文如此)或\`"
(原文如此)'...'
内,\"
可以按原样使用。powershell.exe -command " \""hi\"" " # OK
powershell.exe -command " \`"hi\`" " # OK
powershell.exe -command ' \"hi\" ' # OK
或者,使用脚本块而不是命令 string ,这会绕过引用的麻烦:
powershell.exe -command { "hi" } # OK, but only works when calling from PS
请注意,脚本块技术仅在从PowerShell 调用时才有效,而不是从cmd.exe
调用。
cmd.exe
有自己的引用要求:
值得注意的是,cmd.exe
仅支持""
嵌入双引号(不是`"
);因此,在上述解决方案中,只有
powershell.exe -command " \""hi\"" "
来自cmd.exe
(批处理文件),无需额外转义。
然而,\""
的缺点是\""...\""
之间的内部空白运行会折叠到每个单独的空间。为避免这种情况,请使用\"...\"
,但cmd.exe
会将\"
个实例之间的子字符串视为未加引号,如果该子字符串包含元字符,则会导致命令中断例如|
或&
;例如,powershell.exe -command " \"a|b\" "
;修复您必须单独^
- 逃避以下字符:& | < > ^
powershell.exe -command ' "hi" '
同样很脆弱,因为cmd.exe
不会将'
识别为字符串分隔符,因此嵌入式"..."
之外的所有元字符都会被{{ 1}}本身;例如,cmd.exe
最后,仅使用powershell.exe -command ' "hi" | Measure-Object '
中的""
来嵌入cmd.exe
,有时有效,但不能可靠;例如,"
打印powershell.exe -command " 'Nat ""King"" Cole' "
(缺少结束Nat "King Cole
)
这似乎已在PowerShell Core 中修复。