2012-06-08 5 views
8

Al responder a la pregunta this me encontré con un comportamiento extraño para los que no tengo explicacióneco es la adición de espacio cuando se utiliza con un tubo

for /f "delims=" %a in ('(for /l %z in (1,1,10^) do @echo %z^)') do @echo %a0 

Vas a ver los números 10..100, ahora sólo tiene que añadir la tubería, p.ej a sort o more, cualquiera que sea:

for /f "delims=" %a in ('(for /l %z in (1,1,10^) do @echo %z^)^|sort') do @echo %a0 

habrá espacios entre añaden %a y 0! Parece que el eco-ing algo a través de una tubería añade un espacio al final, se puede ver fácilmente:

>_tempfile echo no space here 
>_tempfile echo and here's a space|more 

e incluso

>_tempfile <nul set /p =also a space|sort 

(probablemente utiliza el eco para imprimir el símbolo)

Esto no ocurre cuando no hay redirección de salida (ya sea en un archivo o para un comando). ¿Es un error o me falta algo? ¿Cómo me deshago del espacio? (Además del truco sucio de despojar el último carácter con var:~0,-1)

Respuesta

7

excelente e interesante pregunta (+1)

El espacio se introduce por el mecanismo de la tubería del analizador CMD, no por género.

Cuando ejecuta un comando usando FOR/F, el comando se ejecuta en su propio shell CMD. Además, cada lado de una tubería se ejecuta en su propio shell CMD. Ver Why does delayed expansion fail when inside a piped block of code? para más información.

De modo que su comando realmente ejemplifica 3 shells CMD, uno para el comando FOR/F, que a su vez crea 2 para cada lado de la tubería.

Puede ver cómo los comandos se analizan y alimentan en el shell de CMD utilizando la variable dinámica% CMDCMDLINE%. Debido a que estamos ejecutando el comando desde la línea de comando, necesitamos escapar al menos un carácter en el nombre de la variable dos veces para que no se expanda hasta que llegue al caparazón CMD más interno.

Este es el comando con los resultados (el principal > es mi símbolo del sistema):

>for /f "delims=" %a in ('(echo %^^^cmdcmdline%^&for /l %z in (1,1,10^) do @echo %z^)^|sort') do @echo %a0 
1 0 
10 0 
2 0 
3 0 
4 0 
5 0 
6 0 
7 0 
8 0 
9 0 
C:\Windows\system32\cmd.exe /S /D /c" (echo %cmdcmdline% & FOR /L %z in (1 1 10) do @ echo %z)" 0 

La última línea de salida es la línea de comandos que se utiliza para el lado izquierdo de la tubería. Puede ver cómo el analizador agregó espacios en varios lugares.

Puede evitar el problema mediante el uso de un simple script por lotes para repetir el valor en lugar del comando ECHO.

echoArgs.bat

@echo(%* 

Ahora cuando se ejecuta este comando se obtiene el resultado deseado

>for /f "delims=" %a in ('(for /l %z in (1,1,10^) do @echoArgs %z^)^|sort') do @echo %a0 
10 
100 
20 
30 
40 
50 
60 
70 
80 
90 

Otro método para evitar el problema es crear una variable con el comando ECHO y escapar de la expansión de la variable apropiadamente.

>set [email protected](echo %z) 

>for /f "delims=" %a in ('(for /l %z in (1,1,10^) do %^^^cmd%^)^|sort') do @echo %a0 
10 
100 
20 
30 
40 
50 
60 
70 
80 
90 

EDITAR

El ejemplo >_tempfile echo and here's a space|more es también interesante. Hay un espacio extra al final del archivo de salida. Pero Chad Nouis tiene razón en que no se está solucionando nada debido a la redirección del lado izquierdo. Cualquier comando podría usarse en el lado derecho y el resultado sería el mismo.

El origen del problema sigue siendo el analizador sintáctico, pero la forma en que el analizador reestructura la comunicación es interesante.

>>_tempfile echo %^cmdcmdline%|rem 

>type _tempfile 
C:\Windows\system32\cmd.exe /S /D /c" echo %cmdcmdline% 1>_tempfile" 

Aviso cómo el cambio de dirección se mueve desde el principio hasta el final del comando, y se añade explícitamente el identificador de archivo de 1. Desde luego, puedes ver de dónde viene el espacio extra.

+0

Ah, eso que aumenta el conocimiento, aumenta la pena_. Nunca pensé que la tubería fuera más que una conexión stdin/stdout. Aceptó su respuesta para una explicación pero no para la solución. Aquí está el sencillo: 'for/f" delims = "% a in ('cmd/c" para/l% z in (1,1,10) do @echo% z "^ | sort') do @echo% a0' –

+0

@ panda-34 - Esa ciertamente es otra solución. – dbenham

+0

+1, soy demasiado tarde, me gusta este tipo de preguntas, pero ya las respondió con su buen análisis – jeb

0

Los operadores de redirección se aplican al comando al que están más cerca, no a toda la línea.

En sus tres casos de prueba, está redirigiendo la extensión de los comandos echo y set a un archivo. Al redirigir la salida estándar, no queda nada para canalizar a more o sort.

En el ejemplo del bucle anidado, creo que hay algunos paréntesis innecesarios que causan dolores de cabeza. Pruebe esto en su lugar:

for /f "delims=" %a in ('for /l %z in (1,1,10^) do @echo %z^|sort') do @echo %a0 
+0

Pero si mira el contenido de _tempfile cuando se creó en el lado izquierdo de una tubería, verá un espacio extra al final. Como expliqué en [mi respuesta] (http://stackoverflow.com/a/10950927/1012053), el analizador de tuberías está introduciendo el espacio. Pero tiene un punto válido en el sentido de que el contenido del archivo temporal no pasó por SORT. Su solución para el caso FOR funciona. Pero puede haber ocasiones en que el lado izquierdo de la tubería sea un bloque de comandos que debe estar en parens. – dbenham

+2

¿Intentó ejecutar su comando? ¿Los resultados parecen ordenados para usted? –

+0

Veo a qué se dirige panda-34. Los parens son necesarios para que todo el resultado FOR se clasifique como un conjunto. Sin los parens, el género se aplica a cada línea individualmente, lo que por supuesto no sirve de nada. * (esta es una versión corregida de mi comentario anterior eliminado) * – dbenham

Cuestiones relacionadas