Pregunta

¿Cómo haría para reemplazar todas las comillas dobles en los parámetros de mi archivo por lotes con comillas dobles escapadas?Este es mi archivo por lotes actual, que expande todos sus parámetros de línea de comando dentro de la cadena:

@echo off
call bash --verbose -c "g++-linux-4.1 %*"

Luego usa esa cadena para realizar una llamada al bash de Cygwin, ejecutando un compilador cruzado de Linux.Desafortunadamente, recibo parámetros como estos en mi archivo por lotes:

"launch-linux-g++.bat" -ftemplate-depth-128 -O3 -finline-functions 
-Wno-inline -Wall  -DNDEBUG   -c 
-o "C:\Users\Me\Documents\Testing\SparseLib\bin\Win32\LinuxRelease\hello.o" 
"c:\Users\Me\Documents\Testing\SparseLib\SparseLib\hello.cpp"

Donde la primera comilla alrededor de la primera ruta pasada finaliza prematuramente la cadena que se pasa a GCC y pasa el resto de los parámetros directamente a bash (lo que falla espectacularmente).

Me imagino que si puedo concatenar los parámetros en una sola cadena y luego escapar de las comillas, debería funcionar bien, pero tengo dificultades para determinar cómo hacerlo.¿Alguien sabe?

¿Fue útil?

Solución 3

Google finalmente se le ocurrió la respuesta. La sintaxis para la sustitución de cadenas en lotes es la siguiente:

set v_myvar=replace me
set v_myvar=%v_myvar:ace=icate%

¿Qué produce "me replicar". Mi script ahora se ve así:

@echo off
set v_params=%*
set v_params=%v_params:"=\"%
call bash -c "g++-linux-4.1 %v_params%"

¿Qué reemplaza todas las instancias de " con \", escaparon adecuadamente para bash.

Otros consejos

El carácter de escape en las secuencias de comandos por lotes es ^. Pero para cadenas entre comillas dobles, duplicar las comillas:

"string with an embedded "" character"

la propia respuesta de elawless resuelve de forma sencilla y eficaz su problema específico:reemplaza todo " instancias en toda la lista de argumentos con \", que es como Bash requiere que se representen comillas dobles dentro de una cadena entre comillas dobles.

Para responder en general a la pregunta de cómo escapar de las comillas dobles dentro de una cadena entre comillas dobles usando cmd.exe, el intérprete de línea de comandos de Windows (ya sea en la línea de comandos - a menudo todavía llamada erróneamente "indicador de DOS" - o en un archivo por lotes):Vea la parte inferior para ver Potencia Shell.

tl; dr:

  • debe usar "" al pasar una cuerda a otro) archivo por lotes y tú puede usar "" con aplicaciones creadas con microsoftCompiladores C/C++/.NET de (cual también aceptar \"), que en Windows incluye Python y Node.js:

    • Ejemplo: foo.bat "We had 3"" of rain."

    • Lo siguiente se aplica únicamente a archivos por lotes:

      • "" es la única forma de obtener el intérprete de comandos (cmd.exe) para tratar toda la cadena entre comillas dobles como una soltero argumento.

      • Lamentablemente, sin embargo, no sólo se conservan las comillas dobles adjuntas (como es habitual), sino también las comillas duplicadas, por lo que obtener la cadena deseada es un proceso de dos pasos;por ejemplo, suponiendo que la cadena entre comillas dobles se pasa como primer argumento, %1:

      • set "str=%~1" elimina las comillas dobles adjuntas; set "str=%str:""="%" luego convierte las comillas dobles duplicadas en simples.
        Asegúrese de utilizar comillas dobles alrededor de las partes de la tarea para evitar interpretaciones no deseadas de los valores.

  • \" es requerido - como única opción - por muchos otros programas, (por ejemplo, Ruby, Perl e incluso el propio PowerShell(!) de Microsoft), pero SU USO NO ES SEGURO:

    • \" es lo que muchos ejecutables e intérpretes tampoco requerir - incluido el propio PowerShell de Microsoft cuando se pasan cadenas desde fuera - o, en el caso de 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 personajes presentan este riesgo: & | < >
      • Por ejemplo, lo siguiente da como resultado la ejecución no deseada de la ver dominio;consulte más abajo para obtener una explicación y el siguiente punto para una solución alternativa:
        • foo.exe "3\" of snow" "& ver."
    • Para Potencia Shell en ventanas, \"" y "^"" son alternativas sólidas, pero limitadas (consulte la sección "Llamar a la CLI de PowerShell ..." a continuación).
  • Si debes usar \", solo hay 3 seguro enfoques, que son, sin embargo, bastante engorroso: Punta del sombrero para ts por su ayuda.

    • Usando (posiblemente selectivo) expansión variable retardada en su archivo por lotes, puede almacenar literal \" en un variable y hacer referencia a esa variable dentro de un "..." cadena usando !var! sintaxis - ver La útil respuesta de T S.

      • El enfoque anterior, a pesar de ser engorroso, tiene la ventaja de que puedes aplicarlo metódicamente y que funciona robustamente, con cualquier entrada.
    • Sólo con cadenas LITERAL (las que NO involucran VARIABLES) se obtiene un enfoque igualmente metódico:categóricamente ^-escapar todo cmd.exe metacaracteres: " & | < > y, si también desea suprimir la expansión de variables, %:
      foo.exe ^"3\^" of snow^" ^"^& ver.^"

    • De lo contrario, debes formule su cadena basándose en el reconocimiento de qué partes de la cadena cmd.exe considera no cotizado debido a una mala interpretación \" como delimitadores de cierre:

      • en literal porciones que contienen metacaracteres de shell: ^-escapar de ellos;usando el ejemplo anterior, es & eso debe ser ^-escapado:
        foo.exe "3\" of snow" "^& ver."

      • en porciones con %...%-referencias de variables de estilo:asegurarse de que cmd.exe los considera parte de un "..." cadena y que los valores de las variables no tienen comillas incrustadas y desequilibradas - lo cual ni siquiera siempre es posible.

Para obtener información general, siga leyendo.


Fondo

Nota:Esto se basa en mis propios experimentos.Déjame saber si me equivoco.

Shells tipo POSIX como Bash en Sistemas tipo Unix tokenizar la lista de argumentos (cadena) antes de pasar argumentos individualmente al programa objetivo:entre otras ampliaciones, dividen la lista de argumentos en palabras individuales (división de palabras) y eliminan los caracteres entre comillas de las palabras resultantes (eliminación de comillas). El programa objetivo recibe un formación de argumentos individuales, con sintáctico comillas eliminadas.

Por el contrario, el intérprete de comandos de Windows aparentemente no tokeniza la lista de argumentos y simplemente pasa el soltero cadena que comprende todo argumentos, incluidas las citas de caracteres.- al programa objetivo.
Sin embargo, alguno El preprocesamiento tiene lugar 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 del siguiente carácter) y las referencias a variables (p. ej., %USERNAME%) son interpolado primero.

Por lo tanto, a diferencia de Unix, es responsabilidad del programa de destino analizar la cadena de argumentos y dividirla en argumentos individuales sin las comillas.De este modo, diferentes programas pueden hipotéticamente requerir diferentes métodos de escape y No existe un único mecanismo de escape que sea garantizado para trabajar con todos los programas - https://stackoverflow.com/a/4094897/45375 contiene excelentes antecedentes sobre la anarquía que es el análisis de la línea de comandos de Windows.

En la práctica, \" es muy común, pero NO SEGURO, como se ha mencionado más arriba:

Desde cmd.exe por sí mismo no reconoce \" como un escapado entre comillas dobles, puede malinterpretar tokens posteriores en la línea de comando como no cotizado y potencialmente interpretarlos como comandos y/o redirecciones de entrada/salida.
En una palabra:El problema surge si alguno de los siguientes caracteres sigue una apertura o desequilibrado \": & | < >;Por ejemplo:

foo.exe "3\" of snow" "& ver."

cmd.exe ve los siguientes tokens, como resultado de una mala interpretación \" como una comilla doble normal:

  • "3\"
  • of
  • snow" "
  • descansar: & ver.

Desde cmd.exe piensa que & ver. es no cotizado, lo interpreta como & (el operador de secuenciación de comandos), seguido del nombre del comando a ejecutar (ver. - el . se ignora; ver informes cmd.exeinformación de la versión).
El efecto general es:

  • Primero, foo.exe se invoca con la primera 3 fichas solamente.
  • Entonces, ordena ver es ejecutado.

Incluso en los casos en los que el comando accidental no causa daño, su comando general no funcionará según lo diseñado, dado que no se le pasan todos los argumentos.

Muchos compiladores/intérpretes reconocen SÓLO \" - por ejemplo, el compilador GNU C/C++, Python, Perl, Ruby, incluso el propio PowerShell de Microsoft cuando se invoca desde cmd.exe - y, excepto PowerShell con \"", para ellos No existe una solución sencilla para este problema.
Esencialmente, tendrías que saber de antemano qué partes de tu línea de comando se malinterpretan como sin comillas y de forma selectiva. ^-escapar de todos los casos de & | < > en esas porciones.

Por el contrario, uso de "" es seguro, pero es Lamentablemente, solo es compatible con archivos por lotes y ejecutables basados ​​en compiladores de Microsoft. (en el caso de archivos por lotes, con las peculiaridades comentadas anteriormente), lo que excluye notablemente Potencia Shell - ver la siguiente sección.


Llamar a la CLI de PowerShell desde cmd.exe o shells tipo POSIX:

Nota:Consulte la sección inferior para saber cómo se manejan las cotizaciones. adentro Potencia Shell.

Potencia Shell, cuando se invoca desde fuera - por ejemplo, de cmd.exe, ya sea desde la línea de comando o desde un archivo por lotes - reconoce solo \" y, en Windows también """ y cuanto más robusto \"" / "^"" (a pesar de internamente Usos de PowerShell ` como carácter de escape en cadenas entre comillas dobles y también acepta "" - ver la sección inferior):

En ventanas, llamando de cmd.exe / un archivo por lotes:

  • "" se rompe, porque fundamentalmente no es compatible:

    • powershell -c " ""ab c"".length " -> error "A la cadena le falta el terminador"
  • \" y """ trabajar en principio, pero no lo son seguro:

    • powershell -c " \"ab c\".length " funciona según lo previsto:sale 5 (nota la 2 espacios)
    • Pero no es seguro porque cmd.exe los metacaracteres rompen el comando, a menos que se escape:
      powershell -c " \"a& c\".length " se rompe, debido a la &, que tendría que escaparse como ^&
  • \"" es seguro, pero normalizar los espacios en blanco interiores, que puede ser no deseado:

    • powershell -c " \""a& c\"".length " salidas 4(!), porque los 2 espacios están normalizados a 1.
  • "^"" es la mejor opción para WindowsPowerShell específicamente, donde es seguro y preserva los espacios en blanco, pero con PowerShell Centro (en Windows) es lo mismo que \"", es decir, espacios en blanco-normalizando.El crédito va a Venryx por descubrir este enfoque.

    • powershell -c " "^""a& c"^"".length " obras:no se rompe - a pesar & - y salidas 5, es decir, espacios en blanco correctamente conservados.

    • Potencia Shell Centro: pwsh -c " "^""a& c"^"".length " obras, pero salidas 4, es decir. normaliza los espacios en blanco, como \"" hace.

En Plataformas tipo Unix (Linux, macOS), llamando Potencia Shell CentroCLI de, pwsh desde un shell tipo POSIX como bash:

debe usar \", que sin embargo es tanto seguro como que preserva los espacios en blanco:

$ pwsh -c " \"a&  c|\".length" # OK: 5

Información relacionada

  • ^ sólo se puede utilizar como carácter de escape en no cotizado instrumentos de cuerda - dentro de cadenas entre comillas dobles, ^ no es especial y se trata como literal.

    • ADVERTENCIA: Uso de ^ en los parámetros pasados ​​al call la declaración está rota (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):
      • ^ casos en entre comillas dobles los valores son inexplicablemente duplicado, alterando el valor que se pasa:por ejemplo, si es variable %v% contiene valor literal a^b, call :foo "%v%" asigna "a^^b"(!) a %1 (el primer parámetro) en la subrutina :foo.
      • No cotizado uso de ^ con call es roto por completo en eso ^ ya no se puede utilizar para escapar de caracteres especiales:p.ej., call foo.cmd a^&b se rompe silenciosamente (en lugar de pasar literal a&b también foo.cmd, como sería el caso sin call) - foo.cmd ni siquiera se invoca (!), al menos en Windows 7.
  • Escapar de un literal % es un caso especial, lamentablemente, que requiere una sintaxis distinta dependiendo de si se especifica una cadena en el línea de comando vs. 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 colocas un ^ al principio, al final o dentro de un nombre de variable en un no cotizado cadena (por ejemplo, echo %^foo%), puede evitar la expansión de variables (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, trabajar de forma segura con valores variables que pueden contener espacios y caracteres especiales:

    • Asignación: Encerrar ambos el nombre de la variable y el valor en un soltero par de comillas dobles;p.ej., set "v=a & b" asigna valor literal a & b a variable %v% (por el contrario, set v="a & b" haría que las comillas dobles formen parte del valor).Escapar literal % instancias como %% (funciona sólo en archivos por lotes; consulte más arriba).
    • Referencia: Referencias de variables entre comillas dobles para asegurarse de que su valor no esté interpolado;p.ej., echo "%v%" no sujeta el valor de %v% a interpolación e 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 operador de secuenciación de comandos y, por lo tanto, intenta ejecutar un comando llamado b.
      También tenga en cuenta la advertencia anterior sobre la reutilización de ^ con el call declaración.
    • Externo Los programas normalmente se encargan de eliminar las comillas dobles alrededor de los parámetros, pero, como se señaló, en los archivos por lotes debe hacerlo usted mismo (p. ej., %~1 para eliminar las comillas dobles adjuntas del primer parámetro) y, lamentablemente, no hay una manera directa que yo sepa de conseguir echo imprimir un valor variable fielmente sin las comillas dobles adjuntas.
      • neil ofertas a forsolución alternativa basada en que funciona siempre y cuando el valor no tenga comillas dobles incrustadas;p.ej.:
        set "var=^&')|;,%!" for /f "delims=" %%v in ("%var%") do echo %%~v
  • cmd.exe hace no reconocer soltero-citas como delimitadores de cadenas: se tratan como literales y, por lo general, no se pueden utilizar para delimitar cadenas con espacios en blanco incrustados;Además, se deduce que los tokens que lindan con las comillas simples y cualquier token intermedio se tratan como sin comillas por cmd.exe e interpretado 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 sí lo hacen. no reconocerlos.
      Sin embargo, incluso si el programa de destino lo admite, no es aconsejable utilizar cadenas entre comillas simples, dado que su contenido no está protegido contra interpretaciones potencialmente no deseadas por parte de cmd.exe.

Citando de dentro Potencia Shell:

WindowsPowerShell es un caparazón mucho más avanzado que cmd.exe, y ha sido parte de Windows durante muchos años (y Núcleo de PowerShell trajo la experiencia PowerShell a macOS y Linux también).

PowerShell funciona consistentemente internamente con respecto a la cita:

  • dentro de cadenas entre comillas dobles, use `" o "" para escapar de comillas dobles
  • dentro de cadenas entre comillas simples, use '' para escapar de 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 Potencia Shell.

(Como se analizó anteriormente, pasar una comilla doble escapada a PowerShell desde fuera requiere \" o, más contundentemente, \"" - nada más funciona).

Lamentablemente, al invocar externo programas de PowerShell, se enfrenta a la necesidad de adaptarse a las propias reglas de cotización de PowerShell y escapar por el objetivo programa:

Este comportamiento problemático también se analiza y resume en este problema de documentos de GitHub

Doble-comillas dentro doble-cadenas entre comillas:

Considere la cuerda "3`" of rain", que PowerShell traduce internamente a literal 3" of rain.

Si desea pasar esta cadena a un programa externo, Tienes que aplicar el escape del programa objetivo. además a 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 cómo ambos `" - para hacer feliz a PowerShell - y el \ - para que el programa objetivo sea feliz - debe estar presente.

La misma lógica se aplica al invocar un archivo por lotes, donde "" debe ser usado:

foo.bat "3`"`" of rain"

Por el contrario, incrustar soltero-comillas en un doble-cadena entre comillas No requiere ningún escape en absoluto.

Soltero-comillas dentro soltero-cadenas entre comillas hacer no requerir extra escapar;considerar '2'' of snow', que es la representación de PowerShell de 2' of snow.

foo.exe '2'' of snow'
foo.bat '2'' of snow'

PowerShell traduce cadenas entre comillas simples a comillas dobles antes de pasarlas al programa de destino.

Sin embargo, doble-comillas dentro soltero-cadenas entre comillas, que no necesitan escapar para Potencia Shell, todavía es necesario escapar para el programa objetivo:

foo.exe '3\" of rain'
foo.bat '3"" of rain'

Potencia Shell v3 introdujo la magia --% opción, llamó al símbolo de parada de análisis, que alivia algo del dolor, al pasar cualquier cosa después de él. no interpretado al programa de destino, excepto para cmd.exe-Referencias de variables de entorno de estilo (p. ej., %USERNAME%), cual son expandido;p.ej.:

foo.exe --% "3\" of rain" -u %USERNAME%

Observe cómo escapar de lo incrustado " como \" solo para el programa de destino (y no también para PowerShell como \`") es suficiente.

Sin embargo, este enfoque:

  • no permite escapando % personajes para evitar expansiones variables del entorno.
  • excluye directo uso 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 hecho que escapar sea mucho más fácil al llamar a programas externos.Sin embargo, ha introducido soporte para cadenas entre comillas simples.

Me pregunto si es fundamentalmente posible en el mundo de Windows cambiar alguna vez al modelo Unix de permitir que el caparazón hacer toda la tokenización y eliminación de cotizaciones predeciblemente, en la delantera, independientemente del programa objetivo, y luego invocar el programa de destino pasando los tokens resultantes.

Como una adición a de mklement0 excelente respuesta :

Casi todos los ejecutables aceptar \" como " escapado. uso seguro en cmd sin embargo es casi sólo es posible utilizando DELAYEDEXPANSION.
Para enviar explícitamente un " literal a algún proceso, asignar \" a una variable de entorno, y luego usar esa variable, siempre que lo necesite para pasar una cotización. Ejemplo:

SETLOCAL ENABLEDELAYEDEXPANSION
set q=\"
child "malicious argument!q!&whoami"

Nota SETLOCAL ENABLEDELAYEDEXPANSION parece funcionar sólo dentro de archivos por lotes. Para obtener DELAYEDEXPANSION en una sesión interactiva, se inicia cmd /V:ON.

Si su batchfile does't trabajo con DELAYEDEXPANSION, puede activar de forma temporal:

::region without DELAYEDEXPANSION

SETLOCAL ENABLEDELAYEDEXPANSION
::region with DELAYEDEXPANSION
set q=\"
echoarg.exe "ab !q! & echo danger"
ENDLOCAL

::region without DELAYEDEXPANSION

Si desea pasar el contenido dinámico de una variable que contiene citas que se evitan como "" puede reemplazar con "" \" en la expansión:

SETLOCAL ENABLEDELAYEDEXPANSION
foo.exe "danger & bar=region with !dynamic_content:""=\"! & danger"
ENDLOCAL

Este reemplazo no está seguro con la expansión de estilo %...%!

En caso de bash -c "g++-linux-4.1 !v_params:"=\"!" OP es la versión segura.


Si por alguna razón aún DELAYEDEXPANSION permitiendo temporalmente no es una opción, sigue leyendo:

El uso de \" desde dentro cmd es un poco más seguro si uno siempre tiene que escapar caracteres especiales, en lugar de sólo a veces. (Es menos probable que se olvide un símbolo de intercalación, si es coherente ...)

Para lograr esto, uno precede a cualquier cita con un acento circunflejo (^"), las cotizaciones que debe alcanzar el proceso hijo como literales, además, deben escaparse con un contragolpe (\^"). todos shell meta caracteres deben escaparon con ^ así, por ejemplo, & => ^&; | => ^|; > => ^>; etc.

Ejemplo:

child ^"malicious argument\^"^&whoami^"

Fuente: Todo el mundo cita argumentos de línea de comando de la manera equivocada , consulte 'Un mejor método para citar'


Para pasar el contenido dinámico, hay que asegurarse de lo siguiente:
La parte del comando que contiene la variable debe ser considerado "citado" por cmd.exe (Esto es imposible si la variable puede contener comillas - no escriba %var:""=\"% ). Para lograr esto, no se escaparon-" la última " antes de la variable y el primer ^ después de la variable. cmd-meta-caracteres entre los dos " no deben escaparse. Ejemplo:

foo.exe ^"danger ^& bar=\"region with %dynamic_content% & danger\"^"

Esto no es seguro, si %dynamic_content% puede contener comillas sin igual.

Por ejemplo para la herramienta de automatización del motor Unreal correr de archivo por lotes - esto funcionó para mí

por ejemplo:  -cmdline = "-Messaging" -device = dispositivo -addcmdline = "- sessionId = sesión -SessionOwner = 'propietario' -SessionName = 'construir' -dataProviderMode = -LogCmds locales -execcmds = 'LogCommodity OFF' = 'Lista de automatización; runtests pruebas + separaron + by + T1 + T2; Salir' "-run

Espero que esto ayude a alguien, que funcionó para mí.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top