精简版
如何将包含引号的参数从 PowerShell 传递到本机命令?
在参数字符串中使用单引号而不是双引号:
→
"/p:Target='Data Source=(local)\SQL;Integrated Security=True'"
/p:Target='Data Source=(local)\SQL;Integrated Security=True'
对参数字符串中的双引号使用反斜杠转义* :
→
'/p:Target=\"Data Source=(local)\SQL;Integrated Security=True\"'
/p:Target="Data Source=(local)\SQL;Integrated Security=True"
如果嵌入的引号仅用于将参数视为单个字符串,而不是参数的必需部分,则可以使用以下内容:
引用整个参数字符串,而不是在参数中嵌入引号:
→
'/p:Target=Data Source=(local)\SQL;Integrated Security=True'
/p:Target=Data Source=(local)\SQL;Integrated Security=True
使用反引号*转义所有 PowerShell 特殊字符(这只能作为内联参数完成):
或
→
/p:Target=`"Data Source=`(local`)\SQL`;Integrated Security=True`"
/p:Target=Data` Source=`(local`)\SQL`;Integrated` Security=True
/p:Target=Data Source=(local)\SQL;Integrated Security=True
完整的命令行示例(使用第二种选择):
PS> [string[]]$arguments = @(
'/target:Deploy',
'/p:UseSandboxSettings=False',
'/p:TargetDatabase=UpdatedTargetDatabase',
'/p:TargetConnectionString=\"Data Source=(local)\SQL;Integrate Security=True\"',
'C:\program files\MyProjectName.dbproj'
)
PS> ./echoargs $arguments
Arg 0 是 </target:Deploy>
Arg 1 是 </p:UseSandboxSettings=False>
Arg 2 是 </p:TargetDatabase=UpdatedTargetDatabase>
Arg 3 是 </p:TargetConnectionString="Data Source=(local)\SQL;Integrate Security=True">
Arg 4 是 <C:\program files\MyProjectName.dbproj>
长版
当人们在遗留 cmd 系统和 PowerShell 之间移动时,调用本机命令会出现很多情况(几乎与“用逗号分隔参数”的陷阱一样多;)。
我在这里尝试并总结了我所知道的关于 PowerShell(v2 和 v3)中的命令调用主题的所有内容,以及我能收集到的所有示例和参考资料。
1)直接调用原生命令
1.1)在最简单的情况下,对于位于环境路径中的可执行文件,可以直接调用该命令,就像调用 PowerShell cmdlet 一样。
PS> Get-ItemProperty echoargs.exe -Name IsReadOnly
...
IsReadOnly : True
PS> attrib echoargs.exe
A R C:\Users\Emperor XLII\EchoArgs.exe
1.2)在环境路径之外,对于特定目录(包括当前目录)中的命令,可以使用命令的完整路径或相对路径。这个想法是让操作员明确声明“我想调用这个文件”,而不是让一个碰巧具有相同名称的任意文件在其位置运行(有关 PowerShell 安全性的更多信息,请参阅此问题)。在需要时未能使用路径将导致“术语无法识别”错误。
PS> echoargs arg
The term 'echoargs' is not recognized as the name of a cmdlet, function, script file,
or operable program...
PS> ./echoargs arg
Arg 0 is <arg>
PS> C:\Windows\system32\attrib.exe echoargs.exe
A R C:\Users\Emperor XLII\EchoArgs.exe
1.3) 如果路径包含特殊字符,可以使用调用运算符或转义字符。例如,可执行文件以数字开头,或位于包含空格的目录中。
PS> $env:Path
...;C:\tools\;...
PS> Copy-Item EchoArgs.exe C:\tools\5pecialCharacter.exe
PS> 5pecialCharacter.exe special character
Bad numeric constant: 5.
PS> & 5pecialCharacter.exe special character
Arg 0 is <special>
Arg 1 is <character>
PS> `5pecialCharacter.exe escaped` character
Arg 0 is <escaped character>
PS> C:\Users\Emperor XLII\EchoArgs.exe path with spaces
The term 'C:\Users\Emperor' is not recognized as the name of a cmdlet, function,
script file, or operable program...
PS> & 'C:\Users\Emperor XLII\EchoArgs.exe' path with spaces
Arg 0 is <path>
Arg 1 is <with>
Arg 2 is <spaces>
PS> C:\Users\Emperor` XLII\EchoArgs.exe escaped` path with` spaces
Arg 0 is <escaped path>
Arg 1 is <with spaces>
2)间接调用本机命令
2.1)当您不以交互方式输入命令,而是将路径存储在变量中时,调用运算符也可用于调用以变量命名的命令。
PS> $command = 'C:\Users\Emperor XLII\EchoArgs.exe'
PS> $command arg
Unexpected token 'arg' in expression or statement.
PS> & $command arg
Arg 0 is <arg>
2.2)传递给命令的参数也可以存储在变量中。变量中的参数可以单独传递,也可以在数组中传递。对于包含空格的变量,PowerShell 将自动转义空格,以便本机命令将其视为单个参数。(请注意,调用运算符将第一个值视为命令,将其余值视为参数;参数不应与命令变量组合。)
PS> $singleArg = 'single arg'
PS> $mushedCommand = "$command $singleArg"
PS> $mushedCommand
C:\Users\Emperor XLII\EchoArgs.exe single arg
PS> & $mushedCommand
The term 'C:\Users\Emperor XLII\EchoArgs.exe single arg' is not recognized as the
name of a cmdlet, function, script file, or operable program...
PS> & $command $singleArg
Arg 0 is <single arg>
PS> $multipleArgs = 'multiple','args'
PS> & $command $multipleArgs
Arg 0 is <multiple>
Arg 1 is <args>
2.3) 数组格式对于为本地命令构建动态参数列表特别有用。对于要识别为不同参数的每个参数,将参数存储在数组变量中很重要,而不仅仅是在一个字符串中混合在一起。(请注意,常用缩写$args
是 PowerShell 中的自动变量,这可能会导致保存在其中的值被覆盖;相反,最好使用描述性名称,$msbuildArgs
以避免命名冲突。)
PS> $mungedArguments = 'initial argument'
PS> $mungedArguments += 'second argument'
PS> $mungedArguments += $(if( $someVariable ) { 'dynamic A' } else { 'dynamic B' })
PS> ./echoargs $mungedArguments
Arg 0 is <initial argumentsecond argumentdynamic B>
PS> $arrayArguments = @('initial argument')
PS> $arrayArguments += 'second argument'
PS> $arrayArguments += $(if( $someVariable ) { 'dynamic A' } else { 'dynamic B' })
PS> ./echoargs $arrayArguments
Arg 0 is <initial argument>
Arg 1 is <second argument>
Arg 2 is <dynamic B>
2.4)此外,对于脚本、函数、cmdlet 等,PowerShell v2 可以使用称为“splatting”的技术发送包含在哈希表中的命名参数,而不必担心参数顺序。这不适用于不参与 PowerShell 对象模型并且只能处理字符串值的本机命令。
PS> $cmdletArgs = @{ Path = 'EchoArgs.exe'; Name = 'IsReadOnly' }
PS> $cmdlet = 'Get-ItemProperty'
PS> & $cmdlet $cmdletArgs # hashtable object passed to cmdlet
Cannot find path 'C:\Users\Emperor XLII\System.Collections.Hashtable'...
PS> & $cmdlet @cmdletArgs # hashtable values passed to cmdlet
...
IsReadOnly : True
PS> ./echoargs @cmdletArgs
Arg 0 is <Name>
Arg 1 is <IsReadOnly>
Arg 2 is <Path>
Arg 3 is <EchoArgs.exe>
3) 使用复杂参数调用本机命令
3.1)对于简单的参数,用于本机命令的自动转义通常就足够了。但是,对于括号、美元符号、空格等,PowerShell 使用的字符需要进行转义才能按原样发送到本机命令,而不需要解析器对其进行解释。这可以通过反引号转义字符 ,`
或将参数放在单引号字符串中来完成。
PS> ./echoargs money=$10.00
Arg 0 is <money=.00>
PS> ./echoargs money=`$10.00
Arg 0 is <money=$10.00>
PS> ./echoargs value=(spaces and parenthesis)
The term 'spaces' is not recognized as the name of a cmdlet, function, script file,
or operable program...
PS> ./echoargs 'value=(spaces and parenthesis)'
Arg 0 is <value=(spaces and parenthesis)>
3.2)不幸的是,当涉及双引号时,这并不是那么简单。作为本机命令的参数处理的一部分,PowerShell 处理器尝试对参数中的所有双引号进行规范化,以便将无引号的参数内容作为单个值传递给本机命令。本机命令参数处理在解析后作为单独的步骤发生,因此正常转义对双引号不起作用;只能使用转义的单引号或反斜杠转义的双引号。
PS> ./echoargs value="double quotes"
Arg 0 is <value=double quotes>
PS> ./echoargs 'value="string double quotes"'
Arg 0 is <value=string>
Arg 1 is <double>
Arg 2 is <quotes>
PS> ./echoargs value=`"escaped double quotes`"
Arg 0 is <value=escaped double quotes>
PS> ./echoargs 'value=\"backslash escaped double quotes\"'
Arg 0 is <value="backslash escaped double quotes">
PS> ./echoargs value='single quotes'
Arg 0 is <value=single quotes>
PS> ./echoargs "value='string single quotes'"
Arg 0 is <value='string single quotes'>
PS> ./echoargs value=`'escaped` single` quotes`'
Arg 0 is <value='escaped single quotes'>
3.3) PowerShell v3 添加了一个新的停止解析符号--%
(请参阅参考资料about_Parsing
)。在复杂参数之前使用时,--%
将按原样传递参数,而不进行任何解析或变量扩展,除了 cmd-like %ENVIRONMENT_VARIABLE%
values。
PS> ./echoargs User:"$env:UserName" "Hash"#555
Arg 0 is <User:Emperor XLII>
Arg 1 is <Hash>
PS> ./echoargs User: "$env:UserName" --% "Hash"#555
Arg 0 is <User:Emperor XLII>
Arg 1 is <Hash#555>
PS> ./echoargs --% User: "%USERNAME%" "Hash"#555
Arg 0 is <User:Emperor XLII>
Arg 1 is <Hash#555>
这也可以用于通过在字符串中传递停止解析符号来消除表示多个参数的单个字符串∗(尽管最好的做法是首先不要混淆参数)。
PS> $user = 'User:"%USERNAME%"'
PS> $hash = 'Hash#' + $hashNumber
PS> $mungedArguments = $user,$hash -join ' '
PS> ./echoargs $mungedArguments
Arg 0 is <User:%USERNAME% Hash#555>
PS> ./echoargs --% $mungedArguments
Arg 0 is <$mungedArguments>
PS> ./echoargs '--%' $mungedArguments
Arg 0 is <User:Emperor XLII>
Arg 1 is <Hash#555>
4) 调试原生命令
有两个关键工具可用于调试 PowerShell 传递给本机命令的参数。
4.1)第一个是PowerShell 社区扩展EchoArgs.exe
中的控制台应用程序,它简单地在尖括号之间写回传递给它的参数(如上面的示例所示)。
4.2)第二个是Trace-Command
cmdlet,可以显示 PowerShell 如何处理管道的许多细节。特别是,NativeCommandParameterBinder
跟踪源将显示 PowerShell 接收并传递给本机命令的内容。
PS> Trace-Command *NativeCommand* { ./echoargs value="double quotes" } -PSHost
DEBUG: NativeCommandParameterBinder : Raw argument string: "value=double quotes"
DEBUG: NativeCommandParameterBinder : Argument 0: value=double quotes
PS> Trace-Command *NativeCommand* { ./echoargs 'value="double quotes"' } -PSHost
DEBUG: NativeCommandParameterBinder : Raw argument string: "value="double quotes""
DEBUG: NativeCommandParameterBinder : Argument 0: value=double
DEBUG: NativeCommandParameterBinder : Argument 1: quotes
PS> Trace-Command *NativeCommand* { ./echoargs value=`"double quotes`" } -PSHost
DEBUG: NativeCommandParameterBinder : Raw argument string: value="double quotes"
DEBUG: NativeCommandParameterBinder : Argument 0: value=double quotes
PS> Trace-Command *NativeCommand* { ./echoargs 'value=\"double quotes\"' } -PSHost
DEBUG: NativeCommandParameterBinder : Raw argument string: "value=\"double quotes\""
DEBUG: NativeCommandParameterBinder : Argument 0: value="double quotes"
其他资源
文章
问题