如何在cmd.exe中正确调用PowerShell.exe同时使用-File与-Command参数?
先说说我遇到的问题
我有个超简单的test.ps1脚本,就一行代码:
echo ok
现在想在cmd里调用PowerShell,同时用-File指定这个脚本,还要用-Command执行额外命令,结果试了两种写法全翻车:
- 先写
-File再写-Command:
结果只输出了powershell.exe -File "test.ps1" -Command "echo test"ok——echo test根本没执行,原来后面的-Command和它的参数被当成了test.ps1的脚本参数,完全没被PowerShell当作命令解析。 - 调换顺序,先写
-Command再写-File:
这次输出成了powershell.exe -Command "echo test" -File "test.ps1"test -File test.ps1——-File和脚本路径直接被当成了echo test的一部分打出来,脚本也没运行。
官方文档还跟实际行为矛盾
查了-Command的官方帮助,里面明确写着:
If the value of Command is a string, it must be the last parameter in the command, any characters typed after command are interpreted as the command arguments.
(如果Command的值是字符串,它必须是命令里的最后一个参数,后面的所有字符都会被当作命令的参数)
但我把-Command放在最后试了,结果还是不行——-Command依旧被当成脚本的参数,这明显和文档描述不符,这绝对是个bug,我已经打算提交给微软了。
我的需求很明确
我要的是正确的命令写法,还有两个硬要求:
- 绝对不能用
-Command的参数来调用脚本(比如powershell.exe -Command "echo test; & \"test.ps1\""这种写法直接pass); - 最终的命令必须能直接放到注册表的
Shell\Open\Command子项里用,就像下面这个可行的示例一样:
而像这种分开写"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -Command "if((Get-ExecutionPolicy ) -ne 'AllSigned') { Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force }; & \"%1\""-Command和-File的写法就不行,因为-Command后面的内容都会被当成它的参数:"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" -Command "if((Get-ExecutionPolicy ) -ne 'AllSigned') { Set-ExecutionPolicy -ExecutionPolicy Bypass -Scope Process -Force }" -File "%1"
我的环境是Windows 10 Pro 64位,用的是系统内置的PowerShell 5.1.19041.1237。
结论:当前版本下没法同时直接用-File和-Command
折腾了半天,我发现这是PowerShell 5.1的设计限制(或者说bug)——-File和-Command是互斥的:
- 只要指定了
-File,PowerShell就会进入脚本执行模式,后面所有参数全变成脚本的位置参数,根本不会再解析其他PowerShell命令行参数; - 如果先写
-Command,后面的任何内容都会被当成-Command字符串的一部分,不会被当成独立参数处理。
符合需求的替代方案
如果必须保留-File调用脚本的方式(比如需要$PSCommandPath这类自动变量正确指向脚本文件),同时要执行额外命令,那只能修改脚本本身,把要执行的命令加到脚本里。但如果不能改脚本,那只能退一步用-Command包裹脚本调用和额外命令的写法,虽然这不符合我一开始的限制,但这是目前唯一能实现预期输出的方式:
powershell.exe -Command "echo test; & \"test.ps1\""
要是你是要放到注册表项里用(比如双击PS脚本时先执行一些操作再运行脚本),官方推荐的就是这种-Command包裹的写法,把%1(代表脚本路径)用&调用就行。
内容的提问来源于stack exchange,提问作者Mario Palumbo

