La propia respuesta de eplawless resuelve de manera simple y efectiva su problema específico: reemplaza todas las "
instancias en la lista completa de argumentos con \"
, que es como Bash requiere que las comillas dobles dentro de una cadena de comillas dobles sean representadas.
Para responder en general a la pregunta de cómo escapar de las comillas dobles dentro de una cadena entre comillas dobles utilizandocmd.exe
, el intérprete de línea de comandos de Windows (ya sea en la línea de comandos, a menudo todavía llamado erróneamente "indicador de DOS", o en un archivo por lotes): Consulte la parte inferior para ver PowerShell .
tl; dr :
Usted debe utilizar""
cuando se pasa una cadena a un (tro) archivo por lotes y se puede utilizar ""
con las aplicaciones creadas con Microsoft C 's / C ++ / compiladores NET. (Que también acepta \"
), que en Windows incluye Python y Node.js :
\"
es requerido - como la única opción - por muchos otros programas , (por ejemplo, Ruby, Perl, e incluso el propio Windows PowerShell de Microsoft (!)), pero SU USO NO ES SEGURO :
\"
es lo que requieren muchos ejecutables e intérpretes , incluido Windows PowerShell, cuando se pasan cadenas desde el exterior , o, en el caso de los compiladores de Microsoft, soporte como alternativa a ""
, en última instancia, sin embargo, depende del programa de destino analizar la lista de argumentos .
- Ejemplo:
foo.exe "We had 3\" of rain."
- SIN EMBARGO, EL USO DE
\"
PUEDE RESULTAR EN LA EJECUCIÓN ARBITRARIA NO DESEADA DE COMANDOS y / o REDIRECCIONES DE ENTRADA / SALIDA :
- Los siguientes caracteres presentan este riesgo:
& | < >
- Por ejemplo, lo siguiente da como resultado una ejecución no intencionada del
ver
comando; vea más abajo para una explicación y el siguiente punto de viñeta para una solución alternativa:
foo.exe "3\" of snow" "& ver."
- Para Windows PowerShell ,
\""
y "^""
son alternativas sólidas pero limitadas (consulte la sección "Llamar a la CLI de PowerShell ..." a continuación).
Si debe usar \"
, solo hay 3 enfoques seguros , que son, sin embargo, bastante engorrosos : Punta del sombrero a TS por su ayuda.
Al usar la expansión de variable retardada (posiblemente selectiva ) en su archivo por lotes, puede almacenar literal \"
en una variable y hacer referencia a esa variable dentro de una "..."
cadena usando la !var!
sintaxis ; consulte la útil respuesta de TS .
- El enfoque anterior, a pesar de ser engorroso, tiene la ventaja de que se puede aplicar metódicamente y que funciona de manera robusta , con cualquier entrada.
Solo con cadenas LITERALES, las que NO involucran VARIABLES, obtiene un enfoque metódico similar: categóricamente, ^
escapar de todos los cmd.exe
metacaracteres: " & | < >
y, si también desea suprimir la expansión de variables %
:
foo.exe ^"3\^" of snow^" ^"^& ver.^"
De lo contrario, debe formular su cadena basándose en el reconocimiento de qué partes de la cadena se cmd.exe
consideran sin comillas debido a una mala interpretación\"
como delimitadores de cierre:
en porciones literales que contienen metacaracteres de shell: ^
-escape de ellos; usando el ejemplo anterior, es &
que se debe ^
escapar:
foo.exe "3\" of snow" "^& ver."
en porciones con %...%
referencias de variable de estilo : asegúrese de que las cmd.exe
considera parte de una "..."
cadena y que los valores de las variables no tienen comillas incrustadas y no balanceadas, lo que ni siquiera siempre es posible .
Para obtener información general, siga leyendo.
Antecedentes
Nota: Esto se basa en mis propios experimentos. Avísame si me equivoco.
Los shells similares a POSIX, como Bash en sistemas similares a Unix, tokenizan la lista de argumentos (cadena) antes de pasar los argumentos individualmente al programa de destino: entre otras expansiones, dividen la lista de argumentos en palabras individuales (división de palabras) y eliminan los caracteres entre comillas del palabras resultantes (eliminación de comillas). El programa de destino recibe una serie de argumentos individuales , sin comillas sintácticas .
Por el contrario, el intérprete de comandos de Windows aparentemente no tokeniza la lista de argumentos y simplemente pasa la cadena única que comprende todos los argumentos, incluidos los caracteres entre comillas. - al programa de destino.
Sin embargo, se realiza un procesamiento previo antes de que la cadena única se pase al programa de destino: ^
caracteres de escape. fuera de las cadenas entre comillas dobles se eliminan (escapan al siguiente carácter), y las referencias de variables (por ejemplo, %USERNAME%
) se interpolan primero.
Por lo tanto, a diferencia de Unix, es responsabilidad del programa de destino analizar para analizar la cadena de argumentos y dividirla en argumentos individuales sin las comillas. Por lo tanto, diferentes programas pueden requerir hipotéticamente diferentes métodos de escape y no existe un mecanismo de escape único que esté garantizado para funcionar con todos los programas : https://stackoverflow.com/a/4094897/45375 contiene una excelente información sobre la anarquía que es la línea de comandos de Windows. análisis.
En la práctica, \"
es muy común, pero NO SEGURO , como se mencionó anteriormente:
Dado que en cmd.exe
sí mismo no se reconoce \"
como una comilla doble de escape , puede malinterpretar los tokens posteriores en la línea de comando como sin comillas y potencialmente interpretarlos como comandos y / o redirecciones de entrada / salida .
En pocas palabras: las superficies de problema, si cualquiera de los siguientes caracteres siguen una abertura o desequilibrada \"
:& | < >
; por ejemplo:
foo.exe "3\" of snow" "& ver."
cmd.exe
ve los siguientes tokens, que resultan de una mala interpretación \"
como una comilla doble regular:
"3\"
of
snow" "
- descanso:
& ver.
Como cmd.exe
piensa que no& ver.
está entre comillas , lo interpreta como &
(el operador de secuencia de comandos), seguido del nombre de un comando a ejecutar ( ver.
- .
se ignora; ver
informa cmd.exe
la información de la versión).
El efecto general es:
- Primero,
foo.exe
se invoca solo con los primeros 3 tokens.
- Luego,
ver
se ejecuta el comando .
Incluso en los casos en que el comando accidental no daña, su comando general no funcionará como fue diseñado, dado que no se le pasan todos los argumentos.
Muchos compiladores / intérpretes SÓLO reconocen\"
, por ejemplo, el compilador GNU C / C ++, Python, Perl, Ruby, incluso el propio Windows PowerShell de Microsoft cuando se invoca desde cmd.exe
, y, excepto (con limitaciones) para Windows PowerShell \""
, para ellos no existe una solución simple a este problema.
Esencialmente, tendría que saber de antemano qué partes de su línea de comando se malinterpretan como sin comillas y, de forma selectiva, ^
escapar de todas las instancias de & | < >
en esas partes.
Por el contrario, el uso de ""
es SEGURO , pero lamentablemente solo es compatible con archivos ejecutables y por lotes basados en el compilador de Microsoft (en el caso de los archivos por lotes, con las peculiaridades mencionadas anteriormente), lo que excluye a PowerShell. ; consulte la siguiente sección.
Llamar a la CLI de PowerShell desde cmd.exe
o shells similares a POSIX:
Nota: Consulte la sección inferior para ver cómo se manejan las cotizaciones dentro de PowerShell.
Cuando se invoca desde el exterior , por ejemplo, desde cmd.exe
, ya sea desde la línea de comando o desde un archivo por lotes:
PowerShell [Core] v6 + ahora reconoce correctamente""
(además de\"
), que es seguro de usar y preserva los espacios en blanco .
pwsh -c " ""a & c"".length "
no se rompe y cede correctamente 6
Windows PowerShell (la edición heredada cuya última versión es 5.1) reconoce solo \"
y, en Windows también """
y el más robusto \""
/"^""
(aunque internamente PowerShell usa`
como carácter de escape en cadenas entre comillas dobles y también acepta""
; consulte la sección inferior):
Llamar a Windows PowerShell desdecmd.exe
/ un archivo por lotes:
""
se rompe , porque es fundamentalmente no compatible:
powershell -c " ""ab c"".length "
-> error "Falta el terminador en la cadena"
\"
y """
funcionan en principio , pero no son seguros :
powershell -c " \"ab c\".length "
funciona según lo previsto: produce 5
(tenga en cuenta los 2 espacios)
- Pero no es seguro, porque los
cmd.exe
metacaracteres rompen el comando, a menos que se escape: se
powershell -c " \"a& c\".length "
rompe , debido al &
, que tendría que escaparse como^&
\""
es seguro , pero normaliza el espacio en blanco interior , lo que puede no ser deseado:
powershell -c " \""a& c\"".length "
salidas 4
(!), porque los 2 espacios están normalizados a 1.
"^""
es la mejor opción para Windows PowerShell específicamente , donde es seguro y conserva los espacios en blanco, pero con PowerShell Core (en Windows) es lo mismo que\""
, es decir, normalización de espacios en blanco . El crédito es para Venryx por descubrir este enfoque.
powershell -c " "^""a& c"^"".length "
funciona : no se rompe - a pesar &
- y produce 5
, es decir, espacios en blanco correctamente conservados.
PowerShell Core : pwsh -c " "^""a& c"^"".length "
funciona , pero genera 4
, es decir, normaliza los espacios en blanco , como lo \""
hace.
En plataformas similares a Unix (Linux, macOS), al llamar a la CLI de PowerShell [Core]pwsh
, desde un shell similar a POSIX como bash
:
Usted debe utilizar\"
, que, sin embargo, es seguro y los espacios en blanco de preservación :
$ pwsh -c " \"a& c|\".length" # OK: 5
Información relacionada
^
solo se puede usar como carácter de escape en cadenas sin comillas ; dentro de cadenas entre comillas dobles, ^
no es especial y se trata como un literal.
- ADVERTENCIA : El uso de los
^
parámetros in pasados a la call
declaración está roto (esto se aplica a ambos usos de call
: invocar otro archivo por lotes o binario y llamar a una subrutina en el mismo archivo por lotes):
^
las instancias en valores entre comillas dobles se duplican inexplicablemente , alterando el valor que se pasa: por ejemplo, si la variable %v%
contiene un valor literal a^b
, call :foo "%v%"
asigna "a^^b"
(!) a %1
(el primer parámetro) en la subrutina :foo
.
- El uso sin comillas de
^
con call
se interrumpe por completo, ya que ^
ya no se puede usar para escapar de caracteres especiales : por ejemplo,call foo.cmd a^&b
se rompe silenciosamente (en lugar de pasar literala&b
tambiénfoo.cmd
, como sería el caso sincall
),foo.cmd
nunca se invoca (!), Al menos en Windows 7.
%
Desafortunadamente, escapar de un literal es un caso especial que requiere una sintaxis distinta dependiendo de si se especifica una cadena en la línea de comando o dentro de un archivo por lotes ; ver https://stackoverflow.com/a/31420292/45375
- En resumen: dentro de un archivo por lotes, use
%%
. En la línea de comando, %
no se puede escapar, pero si coloca un ^
al principio, al final o dentro del nombre de una variable en una cadena sin comillas (por ejemplo, echo %^foo%
), puede evitar la expansión de la variable (interpolación); %
las instancias en la línea de comando que no son parte de una referencia de variable se tratan como literales (por ejemplo, 100%
).
Generalmente, para trabajar de forma segura con valores de variables que pueden contener espacios y caracteres especiales :
- Asignación : Incluya tanto el nombre de la variable como el valor en un solo par de comillas dobles ; por ejemplo,
set "v=a & b"
asigna un valor literal a & b
a la variable %v%
(por el contrario, set v="a & b"
las comillas dobles forman parte del valor). Escapa de las %
instancias literales como %%
(funciona solo en archivos por lotes; consulte más arriba).
- Referencia : referencias de variables entre comillas dobles para asegurarse de que su valor no se interpola; por ejemplo,
echo "%v%"
no somete el valor de %v%
a la interpolación y las impresiones "a & b"
(pero tenga en cuenta que las comillas dobles también se imprimen invariablemente). Por el contrario, echo %v%
pasa literal a
a echo
, interpreta &
como el operador de secuencia de comandos y, por lo tanto, intenta ejecutar un comando llamado b
.
También tenga en cuenta la advertencia anterior sobre el uso de ^
la call
declaración.
- Los programas externos normalmente se encargan de eliminar las comillas dobles adjuntas alrededor de los parámetros, pero, como se señaló, en los archivos por lotes debe hacerlo usted mismo (por ejemplo,
%~1
para eliminar las comillas dobles adjuntas del primer parámetro) y, lamentablemente, no hay manera que conozco echo
para imprimir un valor variable fielmente sin las comillas dobles adjuntas .
- Neil ofrece una
for
solución alternativa que funciona siempre que el valor no tenga comillas dobles incrustadas ; p.ej:
set "var=^&')|;,%!"
for /f "delims=" %%v in ("%var%") do echo %%~v
cmd.exe
no no reconocer individuales -quotes como delimitadores de cadenas - que son tratados como literales y en general no pueden ser usados para cuerdas delimitan con espacios en blanco incrustados; Además, se deduce que los tokens que lindan con las comillas simples y los tokens intermedios se tratan como no citados por cmd.exe
y se interpretan en consecuencia.
- Sin embargo, dado que los programas de destino en última instancia realizan su propio análisis de argumentos, algunos programas como Ruby reconocen cadenas entre comillas simples incluso en Windows; por el contrario, los ejecutables C / C ++, Perl y Python no los reconocen.
Sin embargo, incluso si es compatible con el programa de destino, no es aconsejable utilizar cadenas entre comillas simples, dado que su contenido no está protegido de interpretaciones potencialmente no deseadas por cmd.exe
.
Citando desde dentro de PowerShell:
Windows PowerShell es un shell mucho más avanzado que cmd.exe
, y ha sido parte de Windows durante muchos años (y PowerShell Core también trajo la experiencia de PowerShell a macOS y Linux).
PowerShell funciona de forma coherente internamente con respecto a las citas:
- dentro de cadenas de comillas dobles, use
`"
o ""
para escapar de las comillas dobles
- dentro de cadenas de comillas simples, utilícelo
''
para escapar de las comillas simples
Esto funciona en la línea de comandos de PowerShell y al pasar parámetros a scripts o funciones de PowerShell desde dentro de PowerShell.
(Como se mencionó anteriormente, pasar una comilla doble de escape a PowerShell desde el exterior requiere \"
o, de manera más sólida, \""
nada más funciona).
Lamentablemente, al invocar programas externos desde PowerShell, se enfrenta a la necesidad de adaptarse a las propias reglas de cotización de PowerShell y escapar al programa de destino :
Este comportamiento problemático también se discute y se resume en esta respuesta.
Doble -quotes dentro de dobles cadenas -quoted :
Considere una cadena "3`" of rain"
, que PowerShell traduce internamente a literal 3" of rain
.
Si desea pasar esta cadena a un programa externo, debe aplicar el escape del programa de destino además del de PowerShell ; digamos que desea pasar la cadena a un programa en C, que espera que las comillas dobles incrustadas se escapen como \"
:
foo.exe "3\`" of rain"
Tenga en cuenta que ambos `"
, para hacer feliz a PowerShell, y el \
, para hacer feliz al programa de destino, deben estar presentes.
La misma lógica se aplica a la invocación de un archivo por lotes, donde se ""
debe utilizar:
foo.bat "3`"`" of rain"
Por el contrario, incrustar comillas simples en una cadena de comillas dobles no requiere ningún tipo de escape.
Individuales -quotes dentro individuales cadenas -quoted no no requerir extra de escape; considere'2'' of snow'
, que es la representación de PowerShell de2' of snow
.
foo.exe '2'' of snow'
foo.bat '2'' of snow'
PowerShell traduce cadenas entre comillas simples en comillas dobles antes de pasarlas al programa de destino.
Sin embargo, las comillas dobles dentro de cadenas entre comillas simples , que no necesitan escapar para PowerShell , aún deben escaparse para el programa de destino :
foo.exe '3\" of rain'
foo.bat '3"" of rain'
PowerShell v3 introdujo la --%
opción mágica , llamada símbolo de detener el análisis , que alivia parte del dolor, al pasar cualquier cosa después de que no se interprete al programa de destino, excepto cmd.exe
las referencias de variables de entorno de estilo (por ejemplo, %USERNAME%
), que se expanden; p.ej:
foo.exe --% "3\" of rain" -u %USERNAME%
Tenga en cuenta que es suficiente escapar del incrustado "
como solo \"
para el programa de destino (y no también para PowerShell como \`"
).
Sin embargo, este enfoque:
- no permite caracteres de escape
%
para evitar expansiones de variables de entorno.
- excluye el uso directo de variables y expresiones de PowerShell; en su lugar, la línea de comando debe construirse en una variable de cadena en un primer paso y luego invocarse con
Invoke-Expression
en un segundo.
Por lo tanto, a pesar de sus muchos avances, PowerShell no ha facilitado mucho el escape al llamar a programas externos. Sin embargo, ha introducido soporte para cadenas de comillas simples.
Me pregunto si es fundamentalmente posible en el mundo de Windows cambiar alguna vez al modelo Unix de dejar que el shell haga toda la tokenización y la eliminación de cotizaciones de manera predecible , por adelantado , independientemente del programa de destino , y luego invocar el programa de destino pasando los tokens resultantes .