Saltar al contenido

Línea de comando escapando de comillas simples para PowerShell

Luego de de nuestra prolongada compilación de información dimos con la respuesta este rompecabezas que suelen tener muchos los lectores. Te compartimos la solución y nuestro deseo es serte de gran ayuda.

Solución:

En realidad, esto es mucho más complicado de lo que piensas. Escapar de las comillas anidadas en cadenas pasadas de cmd a PowerShell es un gran dolor de cabeza. Lo que hace que este sea especialmente complicado es que debe realizar el reemplazo en una variable expandida por cmd en el argumento entre comillas pasado a powershell.exe dentro de un argumento entre comillas pasadas a un parámetro de secuencia de comandos de PowerShell. AFAIK cmd no tiene ninguna funcionalidad nativa ni siquiera para los string reemplazos, por lo que necesita PowerShell para hacer el reemplazo por usted.

Si el argumento de la -datos paramater (el contenido en la variable cmd X) no necesariamente tiene que estar entre comillas simples, lo más simple que se puede hacer es entre comillas dobles, de modo que las comillas simples dentro del valor de X no es necesario que te escapen en absoluto. Digo “más simple”, pero incluso eso es un poco complicado. Como indicó Vasili Syrakis, ^ es normalmente el carácter de escape en cmd, pero para escapar de las comillas dobles dentro de una (doble) comilla string, necesitas usar un . Entonces, puede escribir su comando por lotes de esta manera:

C:WindowsSystem32WindowsPowerShellv1.0powershell.exe -ExecutionPolicy Bypass "G:test.ps1 -name "%x%" -data '%y%'"

Eso pasa el siguiente comando a PowerShell:

G:test.ps1 -name "value of x, which may contain 's" -data 'value of y'

Si acaso, X también puede contener caracteres que son caracteres especiales en cadenas interpoladas de PowerShell (", $, o `), entonces se convierte en LOTE más complicado. El problema es ese %X es una variable cmd que se expande mediante cmd antes de que PowerShell tenga la oportunidad de tocarla. Si lo hace entre comillas simples en el comando que está pasando a powershell.exe y contiene una comilla simple, entonces le está dando a la sesión de PowerShell un string que se cancela antes de tiempo, por lo que PowerShell no tiene la oportunidad de realizar ninguna operación en él. Lo siguiente obviamente no funciona, porque el -reemplazar El operador debe recibir un string antes de que pueda reemplazar algo:

'foo'bar' -replace "'", "''"

Por otro lado, si lo cita dos veces, PowerShell interpola el string antes de realizar cualquier reemplazo en él, por lo que si contiene caracteres especiales, se interpretan antes de que un reemplazo pueda escapar de ellos. Busqué por todas partes otras formas de citar cadenas literales en línea (algo equivalente a la de Perl q //, en el que no es necesario escapar nada más que el delimitador de su elección), pero no parece haber nada.

Entonces, la única solución que queda es usar un here string, que requiere un argumento de varias líneas. Eso es complicado en un archivo por lotes, pero se puede hacer:

setlocal EnableDelayedExpansion
set LF=^


set pscommand=G:test.ps1 -name @'!LF!!x!!LF!'@ -data '!y!'
C:WindowsSystem32WindowsPowerShellv1.0powershell.exe -ExecutionPolicy Bypass "!pscommand!"
  • Esto supone que X y y se establecieron anteriormente en el archivo por lotes. Si su aplicación solo puede enviar un comando de una sola línea a cmd, entonces deberá poner lo anterior en un archivo por lotes, agregando las siguientes dos líneas al principio:

    set x=%~1
    set y=%~2
    

    Luego invoque el archivo por lotes como este:

    pathtest.bat "%x%" "%y%"
    

    los ~ elimina las comillas que rodean los argumentos de la línea de comandos. Necesita las comillas para incluir espacios en las variables, pero las comillas también se agregan al valor de la variable. Batch es estúpido de esa manera.

  • Las dos líneas en blanco que siguen set LF=^ son requeridos.

Eso se encarga de las comillas simples que también interpretan todos los demás caracteres en el valor de X literalmente, con una excepción: comillas dobles. Desafortunadamente, si las comillas dobles pueden ser parte del valor como indicó en un comentario, no creo que el problema se pueda solucionar sin el uso de una utilidad de terceros. La razón es que, como se mencionó anteriormente, el lote no tiene una forma nativa de realizar string reemplazos, y el valor de X se expande en cmd antes de que PowerShell lo vea.

POR CIERTO…¡¡BUENA PREGUNTA!!


ACTUALIZAR:

De hecho, resulta que es posible realizar static string reemplazos en cmd. Duncan agregó una respuesta que muestra cómo hacer eso. Es un poco confuso, así que explicaré lo que está sucediendo en la solución de Duncan.

La idea es que %var:hot=cold% se expande al valor de la variable var, con todas las apariciones de hot reemplazadas con cold:

D:Scratchsoscratch>set var=You're such a hot shot!

D:Scratchsoscratch>echo %var%
You're such a hot shot!

D:Scratchsoscratch>echo %var:hot=cold%
You're such a cold scold!

Entonces, en el comando (modificado de la respuesta de Duncan para alinearse con el ejemplo del OP, en aras de la claridad):

powershell G:test.ps1 -name '%x:'=''%' -data '%y:'=''%'

todas las apariciones de ' en las variables X y y son reemplazados por '', y el comando se expande a

powershell G:test.ps1 -name 'a''b' -data 'c''d'

Analicemos el key elemento de eso, '%x:'=''%':

  • Los dos 's al principio y al final son las comillas externas explícitas que se pasan a PowerShell para citar el argumento, es decir, las mismas comillas simples que tenía el OP. %x
  • :'='' es el string reemplazo, lo que indica que ' debe ser reemplazado con ''
  • %x:'=''% se expande al valor de la variable X con ' reemplazado por '', cual es a''b
  • Por lo tanto, todo se expande a 'a''b'

Esta solución escapa a las comillas simples en el valor de la variable de manera mucho más simple que mi solución anterior. Sin embargo, el OP indicó en una actualización que la variable también puede contener comillas dobles, y hasta ahora esta solución todavía no pasa comillas dobles dentro X a PowerShell: los cmd aún los eliminan antes de que PowerShell reciba el comando.

La buena noticia es que con cmd string método de reemplazo, esto se vuelve superable. Ejecute los siguientes comandos cmd después del valor inicial de X ya se ha configurado:

  1. Reemplazar ' con '', para escapar de las comillas simples de PowerShell:

    set x=%x:'=''%
    
  2. Reemplazar " con ", para escapar de las comillas dobles para cmd:

    set x=%x:"="%
    

    El orden de estas dos asignaciones no importa.

  3. Ahora, se puede llamar al script de PowerShell usando la sintaxis que el OP estaba usando en primer lugar (se eliminó la ruta a powershell.exe para que quepa todo en una línea):

    powershell.exe -ExecutionPolicy Bypass "G:test.ps1 -name '%x' -data '%y'"
    

Nuevamente, si la aplicación solo puede enviar un comando de una línea a cmd, estos tres comandos se pueden colocar en un archivo por lotes, y la aplicación puede llamar al archivo por lotes y pasar las variables como se muestra arriba (primera viñeta en mi respuesta original) .

Un punto interesante a tener en cuenta es que si el reemplazo de " con " se realiza en línea en lugar de con un colocar mando, tu no escapar del "s en el string reemplazo, a pesar de que están dentro de una cita doble string, es decir, así:

powershell.exe -ExecutionPolicy Bypass "G:test.ps1 -name '%x:"="' -data '%y'"

no como esto:

powershell.exe -ExecutionPolicy Bypass "G:test.ps1 -name '%x:"=\"' -data '%y'"

No tengo claro la cuestión de si %x y %y son variables CMD (en cuyo caso debería utilizar %x% para sustituirlo en, o se está produciendo una sustitución en su otra aplicación.

Debe escapar de la comilla simple que está pasando a PowerShell duplicándola en la línea de comando CMD.EXE. Puede hacer esto reemplazando las comillas en la variable con dos comillas simples.

Por ejemplo:

C:scripts>set X=a'b

C:scripts>set Y=c'd

C:scripts>powershell .test.ps1 -name '%x:'=''%' '%y:'=''%'
Name is 'a'b'
Data is 'c'd'

dónde test.ps1 contiene:

C:scripts>type test.ps1
param($name,$data)

write-output "Name is '$name'"
write-output "Data is '$data'"

Si la línea de comando que proporcionó se está generando en una aplicación externa, aún debería poder hacerlo asignando el string a una variable primero y usando & para separar los comandos (tenga cuidado de evitar espacios finales en el set mando).

set X=a'b& powershell .test.ps1 -name '%x:'=''%'

El shell CMD admite tanto una forma simple de sustitución como una forma de extraer subcadenas al sustituir variables. Estos solo funcionan cuando se sustituye en una variable, por lo que si desea hacer múltiples sustituciones al mismo tiempo, o sustituir y extraer subcadenas, debe hacer una a la vez configurando las variables con cada paso.

Environment variable substitution has been enhanced as follows:

    %PATH:str1=str2%

would expand the PATH environment variable, substituting each occurrence
of "str1" in the expanded result with "str2".  "str2" can be the empty
string to effectively delete all occurrences of "str1" from the expanded
output.  "str1" can begin with an asterisk, in which case it will match
everything from the beginning of the expanded output to the first
occurrence of the remaining portion of str1.

May also specify substrings for an expansion.

    %PATH:~10,5%

would expand the PATH environment variable, and then use only the 5
characters that begin at the 11th (offset 10) character of the expanded
result.  If the length is not specified, then it defaults to the
remainder of the variable value.  If either number (offset or length) is
negative, then the number used is the length of the environment variable
value added to the offset or length specified.

    %PATH:~-10%

would extract the last 10 characters of the PATH variable.

    %PATH:~0,-2%

would extract all but the last 2 characters of the PATH variable.

valoraciones y reseñas

Si aceptas, tienes la habilidad dejar una reseña acerca de qué le añadirías a esta reseña.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *