2010-03-26 13 views
35

Tengo una lista delimitada de IP que me gustaría procesar individualmente. La longitud de la lista es desconocida por adelantado. ¿Cómo puedo dividir y procesar cada elemento en la lista?dos lotes iterar a través de una cadena delimitada

@echo off 
set servers=127.0.0.1,192.168.0.1,10.100.0.1 

FOR /f "tokens=* delims=," %%a IN ("%servers%") DO call :sub %%a 

:sub 
    echo In subroutine 
    echo %1 
exit /b 

Salidas:

In subroutine 
127.0.0.1 
In subroutine 
ECHO is off. 

Actualización: Usando la respuesta de Franci como referencia, aquí está la solución:

@echo off 
set servers=127.0.0.1,192.168.0.1,10.100.0.1 

call :parse "%servers%" 
goto :end 


:parse 
setlocal 
set list=%1 
set list=%list:"=% 
FOR /f "tokens=1* delims=," %%a IN ("%list%") DO (
    if not "%%a" == "" call :sub %%a 
    if not "%%b" == "" call :parse "%%b" 
) 
endlocal 
exit /b 

:sub 
setlocal 
echo In subroutine 
echo %1 
endlocal 
exit /b 

:end 

Salidas:

In subroutine 
127.0.0.1 
In subroutine 
192.168.0.1 
In subroutine 
10.100.0.1 
+0

Mientras que la solución recursiva es bueno, creo que la sustitución de nueva línea de abajo es más simple. – kybernetikos

Respuesta

26

Actualización: Si se desconoce el número de elementos en la lista, aún es posible analizar todos los elementos con recursión simple en el encabezado de la lista. (He cambiado las direcciones IP a los números simples para la simplicidad de la lista)

Finalmente esa clase Lisp que tomé hace 18 años dio sus frutos ...

@echo off 
setlocal 

set servers=1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24 

echo %servers% 

call :parse "%servers%" 

goto :eos 

:parse 

set list=%1 
set list=%list:"=% 

FOR /f "tokens=1* delims=," %%a IN ("%list%") DO (
    if not "%%a" == "" call :sub %%a 
    if not "%%b" == "" call :parse "%%b" 
) 

goto :eos 

:sub 

echo %1 

goto :eos 

:eos 
endlocal 
+1

Esto no funciona, simplemente imprime "b" y "c" después de la primera dirección IP. Además, el OP indicó que el número de entradas sería desconocido antes de tiempo. Esto puede no ser posible en un archivo de DOS puro, ¡pero me alegra saber que no soy el único que todavía los usa! – harpo

+0

mi error, se olvidó de cambiar el * a los números del token. Y tiene razón - Si el número de tokens no se conoce con anticipación, no es posible en las secuencias de comandos de shell estándar de Windows. –

+0

Exactamente Harpo. La variable de servidores sirve como ejemplo. En la práctica (en mi caso, este script se usará como parte de el esquema de implementación), la variable de servidores se lee como un parámetro de usuario. Saber que pregunto lo imposible también es bueno, así que puedo pasar a enfoques alternativos. – bjaxbjax

4

No puedo creer que esto es ¡tan complicado! Parece que todos querrían dividir una cadena con frecuencia sin saber cuántos tokens hay en ella y sin analizarla por líneas. Por lo general, parece que las personas están escribiendo en un archivo y luego lo siguen o usan un método de varios subcomponentes como estos. ¡Que desastre!

Aquí está mi solución. Es más fácil de seguir y funciona en línea, es decir, no sub rutina. Es bueno crear una lista de nombres de archivos o lo que sea y luego recorrerlos sin tener que escribirlos en un archivo temporal, asegurarme de que no haya colisión de escritura de archivos, eliminar el archivo temporal, etc. Además, no sé cómo regresar. un valor de un sub como si fuera una función, no creo que puedas. Así que tendrías que seguir escribiendo 2 subs con las otras respuestas que veo aquí cada vez que quisieras hacer algo como esto, uno que alimenta al otro. Mi método te permite crear listas fácilmente y recorrerlas tantas veces como quieras a lo largo de la secuencia de comandos.

setlocal enableextensions enabledelayedexpansion 

set SomeList= 
set SomeList=!SomeList!a; 
set SomeList=!SomeList!b; 
set SomeList=!SomeList!c; 
set SomeList=!SomeList!d; 
set SomeList=!SomeList!e; 
set SomeList=!SomeList!f; 
set SomeList=!SomeList!g; 

set List=!SomeList! 
:ProcessList 
FOR /f "tokens=1* delims=;" %%a IN ("!List!") DO ( 
    if "%%a" NEQ "" ( 
     echo %%a 
) 
    if "%%b" NEQ "" (
     set List=%%b 
     goto :ProcessList 
) 
) 

Salida:

a 
b 
c 
d 
e 
f 
g 

Obviamente se puede simplemente intercambiar el eco con algo útil.

3

Esta es una generalización de la solución de Franci, por lo que puede realizar cualquier trabajo en cada elemento de su lista, sin tener copias del código de iteración de la lista.

 
:list_iter 
    rem Usage: call :list_iter :do_sub_for_each_item "CSV of items" 
    set foo=%1 
    set list=%~2 
    for /f "tokens=1* delims=," %%i in ("%list%") do (
     call %foo% %%i 
     if not "%%j" == "" (call :list_iter %foo% "%%j") 
    ) 
    exit /b 

Por ejemplo, se le puede llamar por:

 
call :list_iter :my_foo "one,two,three" 
call :list_iter :my_bar "sun,poo,wee" 
+0

Hice algo similar a esto con la respuesta aceptada. – boileau

88

el comando FOR maneja un número de delimitadores por defecto.En este caso se puede hacer

@echo off 

set servers=127.0.0.1,192.168.0.1,10.100.0.1 

for %%i in (%servers%) do (
    echo %%i 
) 

Si se encuentra con un delimitador que no es compatible de forma nativa, se puede hacer un reemplazo para preparar primero la cadena por lo que es en el formato correcto

@echo off 

set [email protected]@10.100.0.1 
set servers=%servers:@=,% 

for %%i in (%servers%) do (
    echo %%i 
) 

Uso las llamadas recursivas tienen la posibilidad de quedarse sin espacio en la pila una vez que revisa una cierta cantidad de elementos en la lista. También lo prueba, el patrón de recursión parece correr un poco más lento.

6

La dificultad es que el comando for está pensado para trabajar en cadenas de texto de líneas múltiples que normalmente encontrarías en los archivos. La mejor solución que he encontrado para este problema es modificar tu cadena para que sea multilínea.

rem You need to enable delayed expansion, otherwise the new line will interfere 
rem with the for command. 
setlocal ENABLEDELAYEDEXPANSION 
rem The following three lines escape a new line and put it in a variable for 
rem later. 
rem The empty lines are important! 
set newline=^ 


set list=jim alf fred 
for /F %%i in ("%list: =!newline!%") do (echo item is %%i) 

Este ciclo final iterará sobre los elementos en la variable separada por espacios. Lo hace reemplazando el espacio en la variable list con nuevas líneas, y luego haciendo el ciclo normal for.

+0

Solo para agregar, mientras que el for /? La documentación dice que (set) es un conjunto de archivos, pero funciona bien para que sea algo más: p. para% i en (% data%) do echo% procesaré las listas separadas por comas en los datos perfectamente. La respuesta anterior es útil si quieres hacer un procesamiento más complejo basado en tokens con for/F, pero la mayoría de las veces hacer un straight para está bien. – kybernetikos

1

Sí, sé que este es un tema muy antiguo, pero recientemente descubrí un truco interesante que puede realizar la división solicitada de una manera mucho más simple. Aquí está:

set n=1 
set "server[!n!]=%servers:,=" & set /A n+=1 & set "server[!n!]=%" 

Estas dos líneas de dividir la cadena servers en el serverarray y también definir n variable con el número de elementos creados. De esta forma, solo necesita usar un comando for /L del 1 al %n% para procesar todos los elementos. Aquí está el código completo:

@echo off 
setlocal EnableDelayedExpansion 

set servers=127.0.0.1,192.168.0.1,10.100.0.1 

set n=1 
set "server[!n!]=%servers:,=" & set /A n+=1 & set "server[!n!]=%" 

for /L %%i in (1,1,%n%) do echo Process server: !server[%%i]! 

Este método es no sólo más simple y más rápido que cualquier otro método basado en for de comandos, sino que también permite utilizar directamente una cadena de caracteres múltiples como delimitador.

Puede leer más detalles sobre el método de división en this post.

0

Si PowerShell está disponible, usted podría:

C:>set servers=127.0.0.1,192.168.0.1,10.100.0.1 
C:>powershell -NoProfile -Command "('%servers%').Split(',')" 
127.0.0.1 
192.168.0.1 
10.100.0.1 

Otro uso para mostrar una variable PATH legible.

@powershell -NoProfile -Command "($Env:PATH).Split(';')" 

o

powershell -NoProfile -Command "$Env:PATH -split ';'" 
Cuestiones relacionadas