Powershell使用嵌套引号调用msbuild

使用Powershell和Psake为Visual Studio解决scheme创build包和部署。 尝试使用msbuild部署数据库项目 – 这是使用msdos visual studio命令行正确工作

msbuild /target:Deploy /p:UseSandboxSettings=false /p:TargetConnectionString="aConnectionWithSpacesAndSemiColons" "aDatabaseProjectPathWithSpaces" 

从powershell中调用相同的方法调用会导致错误

 & msbuild /target:Deploy /p:UseSandboxSettings=false /p:TargetConnectionString="aConnectionWithSpacesAndSemiColons" "aDatabaseProjectPathWithSpaces" 

与空间有关 – 无法弄清楚如何在PowerShell中复制这个调用 – 示例数据库连接string数据源=。\ SQL2008;初始目录= DocumentExecution;集成安全性= True;

短版

如何将包含引号的参数从PowerShell传递到本地命令?

  • 在参数string中使用单引号而不是双引号:
    " /p:Target= ' Data Source=(local)\SQL;Integrated Security=True '"
    /p:Target= ' Data Source=(local)\SQL;Integrated Security=True '

  • 对参数string*中的双引号使用反斜杠转义:
    ' /p:Target= \" Data Source=(local)\SQL;Integrated Security=True \"'
    /p:Target= " Data Source=(local)\SQL;Integrated Security=True "

如果embedded式引号仅用于将参数视为单个string,而不是参数的必需部分,则可以使用以下内容:

  • 引用整个参数string,而不是在引号中embedded引号:
    ' /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
    /p:Target=Data Source= ( local ) \SQL ; Integrated Security=True /p:Target=Data Source= ( local ) \SQL ; Integrated Security=True

完整的命令行示例(使用第二种select):

 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>
参数2是</ p:TargetDatabase = UpdatedTargetDatabase>
参数3是</ p:TargetConnectionString =“数据源=(本地)\ SQL;集成安全性= True”>
参数4是<C:\ program files \ MyProjectName.dbproj>


长版本

调用本地命令是随着人们在传统的cmd系统和PowerShell之间移动而产生的(几乎与“用逗号分隔参数”一样)。

我试着总结了我所知道的关于PowerShell(v2和v3)中命令调用主题的所有内容,以及所有可以实现的示例和参考。

1)直接调用本地命令

1.1)最简单的是, 对于位于环境path中的可执行文件,可以直接调用该命令 ,就像调用PowerShell cmdlet一样。

 PS> Get-ItemProperty echoargs.exe -Name IsReadOnly ... IsReadOnly : True PS> attrib echoargs.exe ARC:\Users\Emperor XLII\EchoArgs.exe 

1.2)在环境path之外, 对于特定目录(包括当前目录)中的命令,可以使用命令的完整path或相对path 。 这个想法是让运算符明确地声明“我想调用这个文件”,而不是让一个碰巧具有相同名字的任意文件在它的位置运行( 请参阅这个问题以获得关于PowerShell安全性的更多信息 )。 在需要时未能使用path将导致“无法识别的字词”错误。

 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 ARC:\Users\Emperor XLII\EchoArgs.exe 

1.3)如果path包含特殊字符,则可以使用调用操作符或转义字符 。 例如,一个以数字开头的可执行文件,或位于包含空格的目录中。

 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)当你不是以交互方式input命令,而是将path存储在variables中时, 调用操作符也可以用来调用variables中命名的命令

 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)传递给命令的参数也可以存储在variables中。 variables中的参数可以单独传递,也可以传入数组中。 对于包含空格的variables,PowerShell将自动转义空格,以便本地命令将其视为单个参数。 (请注意,调用操作符将第一个值作为命令,其余的值作为参数;参数不应与命令variables组合在一起)。

 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)数组格式对于build立本地命令的dynamic参数列表特别有用。 对于每个参数被识别为一个独立的参数,重要的是参数被存储在一个数组variables中,而不是一起被压缩在一个string中。 (请注意,常见缩写$args是PowerShell中的一个自动variables,可能会导致保存在其中的值被覆盖;相反,最好使用描述性名称(如$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对象模型,只能处理string值。

 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使用的字符需要转义为原样发送到本机命令 ,而不必由parsing器解释。 这可以通过反引号转义字符` ,或者将参数放在单引号string中。

 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处理器会尝试对参数中的所有双引号进行规范化处理,以使参数的内容(无引号)作为单个值传递给本机命令。 本地命令参数处理在parsing之后作为单独的步骤进行,因此正常转义不适用于双引号; 只能使用单引号,否则可以使用反斜线转义的双引号

 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添加了一个新的停止parsing符号--% (请参阅about_Parsing )。 当在复杂参数之前使用时, --%将按原样传递参数,不带任何分析或variables扩展,除了类似于cmd的%ENVIRONMENT_VARIABLE%

 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> 

通过在string*中传递停止parsing符号(尽pipe最好的做法是首先不要参数),也可以使用这个方法来parsing表示多个参数的单个string。

 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)debugging本地命令

debuggingPowerShell传递给本机命令的参数有两个关键工具。

4.1)第一个是EchoArgs.exe ,一个来自PowerShell社区扩展的控制台应用程序,它简单地回传在尖括号之间传递给它的参数(如上面的例子所示)。

4.2)第二个是Trace-Command ,一个可以显示PowerShell如何处理pipe道的许多细节的cmdlet。 特别是, 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" 

其他资源

用品

  • PowerShell V3 CTP2提供更好的parameter passing给EXE
  • 2011-03-10 – 从PowerShell调用遗留/本地应用程序的问题
  • 2010-11-04 – 逃跑的空间
  • 2010-02-01 – 在PowerShell中使用MSDeploy的尝试和磨难
  • 2008-10-17 – 执行需要引号和variables的命令实际上是不可能的 [连接]
  • 2006-05-15 – 无法input包含双引号的参数 [连接]

问题

  • powershell执行外部命令不接受参数
  • 2013-02-20 – 带有双引号的参数没有通过ArgumentList正确传递给Scriptblock
  • 2013-01-02 – 错误:说明=无效的查询
  • 2012-09-18 – 如何在PowerShell中使用参数执行外部程序?
  • 2012-09-10 – 从powershell脚本调用可执行文件(w /参数)
  • 如何从PowerShell运行它时在MSBuild命令行上传递包含分号的属性值?
  • 2012-08-08 – 从PowerShell中调用ruby脚本
  • 2012-08-01 – 括号或引号打破PowerShell命令
  • 2012-07-13 – 使用PowerShell执行源安全获取标签的问题
  • 2012-06-13 – 缺less参数-m在Windows PowerShell中使用svn
  • 2012-06-01 – 带空格和大括号的Powershell命令行参数?
  • 2012-04-18 – Spacedpath,msbuild和psake
  • 使Power shell忽略分号
  • 简单的Powershell Msbuild参数失败
  • 2012-02-10 – 在Powershell中引用疯狂
  • Powershell:使用dynamic创build的参数运行msiexec
  • PowerShell的调用操作符(&)语法和双引号
  • PowerShell – 用空格传递计算path
  • powershell:脚本启动一个程序的参数?
  • 2011-12-20 – Powershell – 调用包含在参数中的禁忌的icacls
  • 2011-12-15 – 通过PowerShell执行时,Msbuild日志不起作用
  • 2011-12-06 – 从Powershell传递参数给InstallUtil
  • 2011-11-23 – 使用Powershell执行带有参数的exe
  • 2011-11-08 – Powershell在启动过程中删除引号
  • 2011-09-16 – 使用引号括起来的variables在PowerShell中执行的命令失败。 为什么?
  • 2011-07-25 – PowerShellparsing引用奇怪 (包括一个简短的分析报价parsing的答案之一)
  • 2011-07-15 – powershell从命令行参数中剥离双引号
  • 2011-06-14 – 在Powershell中,如何从string执行任意本地命令?
  • 2011-06-03 – Powershell使用嵌套引号调用msbuild
  • 2011-05-13 – powershell – 传递参数到EXE
  • 为什么PowerShell脚本无法正确执行这个外部命令?
  • 2011-01-09 – 使用powershell脚本执行EXE文件
  • 2010-12-13 – 一个exe的命令行参数
  • 2010-10-08 – 这个PowerShell命令行引用/转义是怎么回事?
  • 2010-10-05 – 使用powershell从一个空格的目录运行一个exe文件
  • 2010-08-28 – 从Powershell执行存储在variables中的命令
  • 2010-08-17 – 当参数有空格时,如何从powershell调用msdeploy?
  • 2010-04-12 – 如何将PowerShell命令中的引号压缩到可执行文件
  • 2010-01-26 – PowerShell发送多个参数到外部命令
  • 2009-11-04 – 如何用带空格和引号的参数在powershell中运行exe
  • 2009-03-16 – PowerShell – 启动进程和Cmdline开关
  • 2009-01-14 – 如何在Powershell上转义命令行参数?

如果将Start-Process cmdlet与-ArgumentList参数一起使用,可以使这一切变得更加容易。 我很惊讶,这还没有被提及。

例:

 Start-Process -FilePath msbuild.exe -ArgumentList '/target:Deploy /p:UseSandboxSettings=false /p:TargetConnectionString="aConnectionWithSpacesAndSemiColons" "aDatabaseProjectPathWithSpaces"'; 

这里有一个我喜欢使用的方法,它允许进行variablesreplace:

 $ConnectionString = 'aConnectionWithSpacesAndSemiColons'; $DatabaseProjectPath = 'aDatabaseProjectPathWithSpaces'; $MsbuildArguments = '/target:Deploy /p:UseSandboxSettings=false /p:TargetConnectionString="{0}" "{1}"' -f $ConnectionString, $DatabaseProjectPath; Start-Process -FilePath msbuild.exe -ArgumentList $MsbuildArguments; 

把整个参数放在单引号中:

 & msbuild /target:Deploy /p:UseSandboxSettings=false '/p:TargetConnectionString="aConnectionWithSpacesAndSemiColons"' "aDatabaseProjectPathWithSpaces" 

引用的额外级别将意味着PSH不处理与PSH的规则的内容。 (string内的任何单引号需要翻倍 – 这是在PSH单引号string中转义的唯一types)。

@Richard – testing这会产生一个不同的错误,说没有提供有效的项目文件。 我已经通过echoargs pscx helper运行,以显示一些更详细的例子。

  1. 使用包含TargetConnectionString的单个quaatation标记 – Powershell将连接string中的每个空间评估为一个新行:

     & echoargs /target:Deploy /p:UseSandboxSettings=false /p:TargetDatabase=UpdatedTargetDatabase /p:TargetConnectionString='"Data Source=(local)\SQLEXPRESS;Integrated Security=True;Pooling=False"' "C:\program files\MyProjectName.dbproj" Arg 0 is </target:Deploy> Arg 1 is </p:UseSandboxSettings=false> Arg 2 is </p:TargetDatabase=UpdatedTargetDatabase> Arg 3 is </p:TargetConnectionString=Data> Arg 4 is <Source=(local)\SQLEXPRESS;Integrated> Arg 5 is <Security=True;Pooling=False> Arg 6 is <C:\program files\MyProjectName.dbproj> 
  2. 用反引号分隔每个参数重新创build初始问题=连接string周围没有引号:

     & echoargs /target:Deploy ` /p:UseSandboxSettings=false ` 

    c /p:TargetConnectionString="Data Source=(local)\SQLEXPRESS;Integrated Security=True;Pooling=False" “C:\ program files \ MyProjectName.dbproj”

     Arg 0 is </target:Deploy> Arg 1 is </p:UseSandboxSettings=false> Arg 2 is </p:TargetDatabase=UpdatedTargetDatabase> Arg 3 is </p:TargetConnectionString=Data Source=(local)\SQLEXPRESS;Integrated Se curity=True;Pooling=False> Arg 4 is <C:\program files\MyProjectName.dbproj> 
  3. 向引号添加反引号的行为与示例1相同:

     & echoargs /target:Deploy ` /p:UseSandboxSettings=false ` /p:TargetDatabase=UpdatedTargetDatabase ` "/p:TargetConnectionString=`"Data Source=(local)\SQLEXPRESS;Integrated Security=True;Pooling=False"`" ` "C:\program files\MyProjectName.dbproj" 
  4. 使用@运算符试图拆分参数仍然忽略引号:

     $args = @('/target:Deploy','/p:UseSandboxSettings=false',' /p:TargetDatabase=UpdatedTargetDatabase','/p:TargetConnectionString="Data Source=(local)\SQLEXPRESS;Integrated Security=True;Pooling=False"','C:\program files\MyProjectName.dbproj'); $args /target:Deploy /p:UseSandboxSettings=false /p:TargetDatabase=UpdatedTargetDatabase /p:TargetConnectionString="Data Source=(local)\SQLEXPRESS;Integrated Security=True;Pooling=False" C:\program files\MyProjectName.dbproj & echoargs $args 
  5. 使用行分隔符来反转连接string的反引号 – 结果与例1相同:

     & echoargs /target:Deploy ` /p:UseSandboxSettings=false ` /p:TargetDatabase=UpdatedTargetDatabase ` "/p:TargetConnectionString=`"Data Source=(local)\SQLEXPRESS;Integrated Security=True;Pooling=False"`" ` "C:\program files\MyProjectName.dbproj" 

你的问题是PowerShell在将它们传递给命令行应用程序时,不会引号。 我自己碰到这个,认为PowerShell正在吃报价。 只要做到这一点。

 msbuild /target:Deploy /p:UseSandboxSettings=false /p:TargetDatabase=UpdatedTargetDatabase '/p:TargetConnectionString=\"Data Source=(local)\SQLEXPRESS;Integrated Security=True;Pooling=False\"' "C:\program files\MyProjectName.dbproj" 

感谢JohnF的回答,最后我终于明白了这一点。

 echoargs /target:clean`;build`;deploy /p:UseSandboxSettings=false /p:TargetConnectionString=`"Data Source=.`;Integrated Security=True`;Pooling=False`" .\MyProj.dbproj Arg 0 is </target:clean;build;deploy> Arg 1 is </p:UseSandboxSettings=false> Arg 2 is </p:TargetConnectionString=Data Source=.;Integrated Security=True;Pooling=False> Arg 3 is <.\MyProj.dbproj> 

总之,但是在双引号和分号前反斜杠。 less一些(或多一些!)会搞砸了。

从这个答案文章中已经提到,但是使用PowerShell 3,您可以使用 – %来停止PowerShell的正常parsing。

 msbuild --% /target:Deploy /p:UseSandboxSettings=false /p:TargetConnectionString="aConnectionWithSpacesAndSemiColons" "aDatabaseProjectPathWithSpaces"