powershell msbuild psake

Powershell llama a msbuild con comillas anidadas



psake (7)

Version corta

¿Cómo transfiere un argumento que contiene citas a un comando nativo de PowerShell?

  • Use comillas simples en lugar de comillas dobles en la cadena del argumento:
    " /p:Target= '' Data Source=(local)/SQL;Integrated Security=True ''"
    /p:Target= '' Data Source=(local)/SQL;Integrated Security=True ''

  • Utilice la barra invertida-escaping para comillas dobles en la cadena de argumento :
    '' /p:Target= /" Data Source=(local)/SQL;Integrated Security=True /"''
    /p:Target= " Data Source=(local)/SQL;Integrated Security=True "

Si las comillas incrustadas solo se utilizan para tratar el argumento como una sola cadena, en lugar de ser una parte requerida del parámetro, se puede usar lo siguiente:

  • Cita toda la cadena del argumento, en lugar de incrustar comillas en el argumento:
    '' /p:Target=Data Source=(local)/SQL;Integrated Security=True ''
    /p:Target=Data Source=(local)/SQL;Integrated Security=True

  • Escape a todos los caracteres especiales de PowerShell con retrocesos (esto solo se puede hacer como un argumento en línea):
    /p:Target= `" Data Source= `( local `) /SQL `; Integrated Security=True `"
    o /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


Ejemplo completo de línea de comando (usando la segunda alternativa):

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 is </target:Deploy> Arg 1 is </p:UseSandboxSettings=False> Arg 2 is </p:TargetDatabase=UpdatedTargetDatabase> Arg 3 is </p:TargetConnectionString="Data Source=(local)/SQL;Integrate Security=True"> Arg 4 is <C:/program files/MyProjectName.dbproj>


Versión larga

Llamar comandos nativos es algo que surge bastante a medida que la gente se mueve entre el sistema de cmd heredado y PowerShell (casi tanto como el "parámetro de separación con comas" gotcha;).

He intentado resumir todo lo que sé sobre el tema de la invocación de comandos en PowerShell (v2 y v3) aquí, junto con todos los ejemplos y referencias que puedo reunir.


1) Llamar comandos nativos directamente

1.1) En su forma más simple, para un ejecutable ubicado en la ruta del entorno, el comando se puede invocar directamente , tal como lo haría con un cmdlet de PowerShell.

PS> Get-ItemProperty echoargs.exe -Name IsReadOnly ... IsReadOnly : True PS> attrib echoargs.exe A R C:/Users/Emperor XLII/EchoArgs.exe


1.2) Fuera de la ruta del entorno, para los comandos en un directorio específico (incluido el actual), se puede usar la ruta completa o relativa al comando . La idea es hacer que el operador declare explícitamente "Deseo invocar este archivo", en lugar de dejar que un archivo arbitrario que tenga el mismo nombre se ejecute en su lugar ( consulte esta pregunta para obtener más información sobre la seguridad de PowerShell ). Si no se utiliza una ruta cuando es necesaria, se producirá un error de "término no reconocido".

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) Si una ruta contiene caracteres especiales, se puede usar el operador de llamada o el carácter de escape . Por ejemplo, un ejecutable que comienza con un número, o que se encuentra en un directorio que contiene un espacio.

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) Llamar comandos nativos indirectamente

2.1) Cuando no está escribiendo un comando de manera interactiva, sino que tiene la ruta almacenada en una variable, el operador de llamada también se puede usar para invocar el comando nombrado en una variable .

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) Los argumentos pasados ​​a un comando también se pueden almacenar en variables. Los argumentos en las variables se pueden pasar individualmente o en una matriz. Para las variables que contienen espacios, PowerShell escapará automáticamente de los espacios para que el comando nativo lo vea como un único argumento. (Tenga en cuenta que el operador de llamada trata el primer valor como el comando y los valores restantes como argumentos; los argumentos no se deben combinar con la variable de comando).

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) El formato de matriz es especialmente útil para compilar una lista dinámica de argumentos para un comando nativo. Para que cada argumento se reconozca como un parámetro distinto, es importante que los argumentos se almacenen en una variable de matriz, y no solo se junten en una cadena. (Tenga en cuenta que la abreviatura común $args es una variable automática en PowerShell, que puede hacer que los valores guardados se sobrescriban; en su lugar, es mejor usar un nombre descriptivo como $msbuildArgs para evitar el conflicto de nomenclatura).

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) Además, para scripts, funciones, cmdlets y similares, PowerShell v2 puede enviar argumentos con nombre contenidos en una tabla hash usando una técnica llamada "splatting", sin tener que preocuparse por el orden de los parámetros. Esto no funciona con comandos nativos, que no participan en el modelo de objetos de PowerShell y solo pueden manejar valores de cadena.

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) Llamar comandos nativos con argumentos complicados

3.1) Para argumentos simples, el escape automático utilizado para comandos nativos es generalmente suficiente. Sin embargo, para paréntesis, signos de dólar, espacios y demás, los caracteres utilizados por PowerShell deben escaparse para enviarse como están a comandos nativos , sin que el analizador los interprete. Esto se puede hacer con el carácter de escape "backtick ` , o colocando el argumento dentro de una cadena de comillas simples.

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) Lamentablemente, esto no es tan simple cuando se trata de comillas dobles. Como parte del procesamiento de argumentos para comandos nativos, el procesador PowerShell intenta normalizar todas las comillas dobles en un argumento para que el contenido del argumento, sin comillas, se pase como un valor único al comando nativo. El procesamiento del parámetro de comando nativo se produce como un paso separado después del análisis, por lo que el escape normal no funcionará para las comillas dobles; solo se pueden usar comillas simples escapadas, o comillas dobles escapadas .

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 agregó un nuevo símbolo de análisis de detención --% (consulte about_Parsing ). Cuando se usa antes de argumentos complicados, --% pasará argumentos tal cual, sin ningún tipo de análisis ni expansión de variables, a excepción de los valores de %ENVIRONMENT_VARIABLE% cmd .

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>

Esto también se puede usar para definir una sola cadena que representa múltiples argumentos, pasando el símbolo de detención de análisis en una cadena (aunque la mejor práctica es no comunicar argumentos en primer lugar).

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) Depuración de comandos nativos

Hay dos herramientas clave para depurar los argumentos que PowerShell pasa a los comandos nativos.

4.1) El primero es EchoArgs.exe , una aplicación de consola de PowerShell Community Extensions que simplemente recupera los argumentos que se le pasaron entre corchetes angulares (como se muestra en los ejemplos anteriores).

4.2) El segundo es Trace-Command , un cmdlet que puede mostrar muchos detalles de cómo PowerShell procesa una tubería. En particular, el NativeCommandParameterBinder rastreo NativeCommandParameterBinder mostrará lo que PowerShell recibe y lo NativeCommandParameterBinder a un comando nativo.

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"


Otros recursos

Artículos

Preguntas

  • 2013-09-11 - powershell ejecuta un comando externo que no acepta el parámetro
  • 2013-02-20 - Los parámetros con comillas dobles no se pasan correctamente a Scriptblock por ArgumentList
  • 2013-01-02 - ERROR: Descripción = consulta no válida
  • 2012-09-18 - ¿Cómo puedo ejecutar un programa externo con parámetros en PowerShell?
  • 2012-09-10 - Invoca ejecutable (w / parameters) del script de powershell
  • 2012-08-16 - ¿Cómo paso un valor de propiedad que contiene un punto y coma en la línea de comandos de MSBuild cuando lo ejecuto desde PowerShell?
  • 08/08/2012 - Llamada a la secuencia de comandos ruby ​​de Powershell
  • 2012-08-01 - Corchetes o comillas que rompen un comando de powershell
  • 13.03.2013 - Problemas al usar powershell para realizar una etiqueta de seguridad get get by
  • 2012-06-13 - Falta el argumento -m usando svn en windows powershell
  • 2012-06-01 - argumento de la línea de comandos de Powershell con espacios y llaves?
  • 2012-04-18 - Rutas espaciadas, msbuild y psake
  • 2012-04-12 - Make Power shell ignora el punto y coma
  • 2012-03-08 - Simple Powershell Msbuild con parámetro falla
  • 2012-02-10 - Quote Madness en Powershell
  • 2012-01-18 - Powershell: ejecute msiexec con parámetros creados dinámicamente
  • 2012-01-18 - Operador de llamadas de PowerShell (&) sintaxis y comillas dobles
  • 2012-01-16 - PowerShell: paso de rutas calculadas con espacios
  • 2012-01-09 - powershell: secuencia de comandos para iniciar un programa con parámetros?
  • 2011-12-20 - Powershell - llamadas icacls con parantheses incluidos en los parámetros
  • 2011-12-15 - El registro de Msbuild no funciona cuando se ejecuta a través de powershell
  • 06/12/2011 - Pasa los parámetros a InstallUtil de Powershell
  • 2011-11-23 - Ejecutando un exe con argumentos usando Powershell
  • 2011-11-08 - Powershell elimina las cotizaciones cuando comienza el proceso
  • 2011-09-16 - Los comandos ejecutados en PowerShell con variables rodeadas de comillas fallan. ¿Por qué?
  • 25-07-2011: las citas de análisis de Powershell son extrañas (incluye un análisis breve del análisis de comillas en una de las respuestas)
  • 15.07.2015 - powershell eliminando comillas dobles de los argumentos de línea de comando
  • 2011-06-14 - En Powershell, ¿cómo ejecutas un comando nativo arbitrario desde una cadena?
  • 2011-06-03 - share
  • 2011-05-13 - powershell - pasando parámetros a exe
  • 2011-03-02 - ¿Por qué este script de PowerShell no puede ejecutar este comando externo correctamente?
  • 2011-01-09 - Ejecutando un archivo EXE usando script powershell
  • 12-12-2010 - argumentos de línea de comandos a un exe
  • 2010-10-08 - ¿Qué pasa con esta línea de comando de PowerShell que cita / escapa?
  • 2010-10-05 - Ejecutando un exe usando powershell desde un directorio con espacios en él
  • 2010-08-28 - Ejecución de un comando almacenado en una variable de Powershell
  • 17-08-2010 - ¿Cómo llamas a msdeploy desde powershell cuando los parámetros tienen espacios?
  • 2010-04-12 - Cómo suprimir las cotizaciones en los comandos de Powershell a ejecutables
  • 2010-01-26 - powershell envía múltiples parámetros a un comando externo
  • 2009-11-04 - Cómo ejecutar exe en PowerShell con parámetros con espacios y comillas
  • 16-03-2009 - PowerShell - Conmutadores de arranque y de línea de proceso
  • 2009-01-14 - ¿Cómo escapar de los argumentos de línea de comando en Powershell?

Usando Powershell y Psake para crear paquetes e implementación para una solución de estudio visual. Intentando implementar un proyecto de base de datos usando msbuild, que funciona correctamente usando la línea de comando de msdos visual studio

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

la misma llamada al método produce un error cuando se llama desde powershell

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

relacionado con los espacios: no se puede encontrar la forma de replicar esta llamada en powershell - connectionbase de la base de datos de muestra Data Source =. / SQL2008; Initial Catalog = DocumentExecution; Integrated Security = True;


@Richard: al probar esto, se genera un error diferente que indica que no se proporciona un archivo de proyecto válido. He ejecutado esto a través de echoargs pscx helper para mostrar algunos ejemplos más detallados.

  1. Con las marcas de quotación únicas que envuelven el TargetConnectionString, Powershell evalúa cada espacio en la conexión como una nueva línea:

    & 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. Separar cada parámetro con barras invertidas recrea el problema inicial = sin comillas alrededor de la conexión:

    & 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. La adición de "backticks" a las comillas se comporta de la misma manera que en el ejemplo 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. El uso del operador @ para intentar dividir los parámetros aún ignora las comillas:

    $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. Backticks para escapar de la conexión de línea usando separadores de línea - mismos resultados que en el ejemplo 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"


Gracias a la respuesta de JohnF, finalmente pude descifrarlo.

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>

En resumen, pero backticks en frente de las comillas dobles Y los puntos y comas. Cualquier cosa menos (¡o más!) Lo arruinará.


Pon el parámetro completo en comillas simples:

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

El nivel extra de cotización significará que PSH no procesa el contenido con las reglas de PSH. (Cualquier comilla simple dentro de la cadena debe duplicarse; este es el único tipo de escape en una cadena PSH de una sola cita).


Se menciona en los artículos de esta respuesta , pero con PowerShell 3 puede usar -% para detener el análisis normal que hace PowerShell.

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


Su problema es que PowerShell no escapa a las comillas cuando las pasa a las aplicaciones de línea de comandos. Me encontré con esto y pensé que PowerShell estaba comiendo las comillas. Solo haz esto.

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"


Todo esto podría ser mucho más fácil si usa el cmdlet Start-Process con el parámetro -ArgumentList. Me sorprende que esto no se haya mencionado ya.

Ejemplo:

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

Este es un método que me gusta usar un poco mejor, que permite la sustitución de variables:

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