usebackq ss64 loop for delims commands batch bat batch-file for-loop cmd quotes

batch file - ss64 - ¿Cómo manejar el paréntesis de cierre en nombres de ruta en un bucle for?



ss64 cmd (2)

Hice algunas pruebas para descubrir por qué el método directo que usaste al principio no funcionó. Primero creé un directorio llamado test (this) y dentro de él creé un archivo por lotes llamado prog.bat :

>dir /b test (this) >dir /b "test (this)" prog.bat >type "test (this)/prog.bat" @echo off echo/One - Line one of prog echo/Two - Line two of prog echo/Three - Line three of prog

Luego, primero intenté acceder al contenido de dicho archivo con for /f . Tenga en cuenta que se useback opción de useback porque el nombre del archivo incluye espacios y debe estar entre comillas:

>for /f "usebackq tokens=1" %G in ("test (this)/prog.bat") do @echo %G @echo echo/One echo/Two echo/Three

Los resultados previos prueban que la ruta fue dada correctamente. Ahora, para ejecutar el archivo por lotes en lugar de leerlo, simplemente encierre su nombre en comillas, ¿no?

>for /f "usebackq tokens=1" %G in (`"test (this)/prog.bat"`) do @echo %G ''test'' is not recognized as an internal or external command, operable program or batch file.

Luego, como el mensaje decía que la test es el nombre del comando no encontrado, creé un archivo por lotes llamado test.bat :

>type test.bat @echo off echo Test.batFileLine1 echo Test.batFileLine2 echo Test.batFileLine2 >for /f "usebackq tokens=1" %G in (`"test (this)/prog.bat"`) do @echo %G Test.batFileLine1 Test.batFileLine2 Test.batFileLine2

Aha! Los resultados anteriores muestran que en este caso las cotizaciones se ignoran y solo se ejecuta test (this)/prog.bat , ¿no?

>for /f "usebackq tokens=1" %G in (`test (this)/prog.bat`) do @echo %G /prog.bat`) was unexpected at this time.

¿Ahora que? Bueno, el problema AHORA está relacionado con los paréntesis:

>for /f "usebackq tokens=1" %G in (`test ^(this^)/prog.bat`) do @echo %G Test.batFileLine1 Test.batFileLine2 Test.batFileLine2

Mi conclusión es que el comando for /f tiene un error cuando ambas comillas y comillas se usan en combinación con la opción "usebackq" y el nombre del archivo tiene espacios, y que este error se omite si se usa una expansión de la variable retrasada.

Tengo un nombre de ruta larga para un programa que debo ejecutar en un ciclo for / f, que incluye un paréntesis de cierre ")", y desde el cual necesito analizar el resultado:

for /f "tokens=1" %%G in (''"C:/Documents and Settings/myaccount/Desktop/Test_release (x86)/program.exe" list'') do (echo Will do something with %%G)

... donde ''list'' es un parámetro pasado a mi programa. Me sale el error "''C: / Documents'' no se reconoce como un comando interno o externo, programa operable o archivo por lotes."

Sé que el problema es que el paréntesis de cierre cierra el bloque "a favor", por lo que las comillas dobles finales no se "ven", por lo que el nombre de la ruta larga ya no se incluye entre comillas dobles. Lo que no entiendo es por qué está sucediendo esto, ya que mi camino está entre comillas dobles. También probé la opción usebackq:

for /f "usebackq tokens=1" %%G in (`"C:/Documents and Settings/myaccount/Desktop/Test_release (x86)/program.exe" list`) do (echo Will do something with %%G)

... sin mejores resultados. Traté de escapar así "^)" o como este "^^)", no hay nada que hacer. Intenté doblar las comillas dobles:

for /f "tokens=1" %%G in (''""C:/Documents and Settings/myaccount/Desktop/Test_release (x86)/program.exe"" list'') do (echo Will do something with %%G)

Todavía no funciona.

Además, de hecho estoy usando una variable que contiene la ruta, que no se conoce de antemano (construida a partir de% CD%) y se activa EnableDelayedExpansion . Probé la expansión diferida (que solucionó problemas similares en otras situaciones) para evitar la expansión de la variable en el tiempo de lectura y retrasarla en el momento de la ejecución:

setlocal EnableDelayedExpansion set _var=%CD%/program.exe @REM _var now contains C:/Documents and Settings/myaccount/Desktop/Test_release (x86)/program.exe for /f "tokens=1" %%G in (''"!_var!" list'') do (echo %%G) endlocal

Aún no funciona, no entiendo por qué.

Pero , doblando las comillas dobles con la expansión demorada en el código anterior:

for /f "tokens=1" %%G in (''""!_var!"" list'') do (echo %%G)

¡funciona! ... ¿por qué ... por qué tener que hacer esto? ¿Qué efecto tiene? No entiendo. También me temo que puede causar un problema en algunas circunstancias específicas ...

¿Alguna idea?


Los comentarios en las respuestas a esta pregunta indican que XP ofrece un comportamiento diferente, luego las versiones de Windows más nuevas.

Existe un error FOR / F conocido en XP: http://www.dostips.com/forum/viewtopic.php?p=9062#p9062 . Pero este problema no está relacionado con ese error.

El problema real proviene de cómo FOR / F ejecuta un comando en la cláusula IN (). Utiliza el CMD /C command (ver ¿Cómo funciona el intérprete de comandos de Windows (CMD.EXE)?

Puede observar este comportamiento agregando esta línea al ejemplo PROG.BAT de Aacini.

echo cmdcmdline=%cmdcmdline%

El siguiente número trata de cómo CMD trata con las comillas que aparecen en el comando / C y por qué XP se comporta de manera diferente que las versiones de Windows más recientes.

Este comando falla en XP, pero tiene éxito en Vista y más allá:

for /f "delims=" %a in (''"test (this)/prog" args'') do @echo %a

El comando que FOR intenta ejecutar (% cmdcmdline%) es el mismo en ambas versiones (sin tener en cuenta las diferencias en% COMSPEC%):

C:/Windows/system32/cmd.exe /c "test (this)/prog" args

XP tiene un defecto de diseño CMD en la forma en que trata las cotizaciones. El defecto está incluso documentado (pero no se reconoce como un defecto). Vista y más allá arreglan parcialmente el defecto de diseño, pero no se molestan en corregir la documentación.

Aquí hay un extracto de HELP CMD

If /C or /K is specified, then the remainder of the command line after the switch is processed as a command line, where the following logic is used to process quote (") characters: 1. If all of the following conditions are met, then quote characters on the command line are preserved: - no /S switch - exactly two quote characters - no special characters between the two quote characters, where special is one of: &<>()@^| - there are one or more whitespace characters between the two quote characters - the string between the two quote characters is the name of an executable file. 2. Otherwise, old behavior is to see if the first character is a quote character and if so, strip the leading character and remove the last quote character on the command line, preserving any text after the last quote character.

Queremos que CMD siga la regla 1 para que las cotizaciones se conserven, pero ( y ) violen la restricción de caracteres especiales en XP, por lo que se sigue la regla 2 y la CMD intenta ejecutar

test (this)/prog args

¡Debería ser bastante obvio por qué esto falla!

No puedo pensar en ninguna razón por la cual la restricción del carácter especial existe en la regla 1. Derrota todo el propósito de lo que MS está intentando hacer.

Aparentemente, la falla de diseño está parcialmente reparada en Vista y más allá, pero no han actualizado la documentación de HELP. Vista ignora los caracteres especiales ( y ) y procesa el comando utilizando la regla 1, las comillas se conservan y todo funciona.

Actualización del 2015-05-17: Desafortunadamente, Vista y sus versiones posteriores aún tratan a @ , ^ , y & como caracteres especiales, a pesar de que son caracteres válidos dentro de los nombres de los archivos. Por supuesto < , > y | se tratan como caracteres especiales, pero de todos modos no son válidos en un nombre de archivo. Entonces, para Vista y más allá, la documentación para la regla 1 debe leer where special is one of: &<>@^| .

He rastreado el comportamiento que todos han documentado, y todo es coherente con lo anterior.

Hay una forma de ejecutar el comando en XP sin usar una variable de expansión retardada, y es compatible con Vista y más allá.

for /f "delims=" %a in (''^""test (this)/prog" args^"'') do @echo %a

Las comillas de apertura y cierre se escapan para que el ) no interfiera con el analizador FOR. El comando que se ejecuta para la cláusula IN () es

C:/Windows/system32/cmd.exe /c ""test (this)/prog" args"

Tanto XP como Vista siguen la regla 2 porque hay más de dos comillas, por lo que CMD ejecuta

"test (this)/prog" args

y todo funciona!

El resto de esta respuesta está desactualizada pero preservada para dar contexto a los comentarios existentes.

Su primer código de ejemplo debería funcionar; no puede (hacer que no debería) dar el mensaje de error que describes. El mensaje de error interrumpe la ruta en el primer espacio, lo que implica que la ruta no se citó ni se escapó. Pero está "seguro" de que fue citado.

La clave del problema son tres elementos de información cerca del final de su publicación:

  1. En realidad estás usando una variable con expansión retardada

  2. esto no funciona: for /f "tokens=1" %%G in (''"!_var!" list'') do (echo %%G)

  3. esto funciona: for /f "tokens=1" %%G in (''""!_var!"" list'') do (echo %%G)

Si el valor de var ya está citado, obtendrá el comportamiento que está describiendo.

El valor de su var debe ser "C:/Documents and Settings/myaccount/Desktop/Test_release (x86)/program.exe" , incluidas las comillas.

Para hacer esta explicación más legible, acortaré el camino a "test (this)/prog.exe"

"!var!" falla porque se expande a ""test (this)/prog.exe"" , que efectivamente anula la comilla. La cadena tiene tres áreas, dos que se citan, y una en el medio que no es:

" área cotizada vacía " ruta sin cita " área cotizada vacía "

""!var!"" funciona porque se expande a """test (this)/prog.exe""" y la ruta ahora se vuelve a citar. Ahora hay cinco áreas dentro de la cadena:

" área cotizada vacía " área vacía sin cotización " ruta citada " área vacía sin cotización " área cotizada vacía "

La respuesta simple sobre cómo debe proceder:

Si el valor de var ya está citado, simplemente use !var! Editar- eso no funciona en XP: "! Var!" funciona en ambos

Si el valor de var no está entre comillas, utiliza "!var!" Editar- eso no funciona en XP: ""! Var! "" Funciona en ambos