2010-03-26 5 views
35

J'ai une liste délimitée d'adresses IP que je voudrais traiter individuellement. La longueur de la liste est inconnue à l'avance. Comment séparer et traiter chaque élément de la liste?dos batch itérer dans une chaîne délimitée

@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 

Sorties:

In subroutine 
127.0.0.1 
In subroutine 
ECHO is off. 

Mise à jour: En utilisant la réponse de Franci comme référence, voici la solution:

@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 

Sorties:

In subroutine 
127.0.0.1 
In subroutine 
192.168.0.1 
In subroutine 
10.100.0.1 
+0

Bien que la solution récursive est bon, je pense que le remplacement de la nouvelle ligne ci-dessous un est plus simple. – kybernetikos

Répondre

26

Mise à jour: Si le nombre d'éléments dans la liste n'est pas connu, il est toujours possible d'analyser tous les éléments avec une récursion simple sur la tête de la liste. (Je l'ai changé les adresses IP à des numéros simples pour la simplicité de la liste)

Enfin cette classe Lisp j'ai pris il y a 18 ans a payé ...

@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

Cela ne fonctionne pas - il imprime simplement "b" et "c" après la première adresse IP. En outre, le PO a déclaré que le nombre d'entrées serait inconnu à l'avance. Cela peut ne pas être possible dans un fichier batch DOS pur - mais je suis heureux de savoir que je ne suis pas le seul à les utiliser! – harpo

+0

mon erreur, j'ai oublié de changer le * pour les numéros de jeton. - si le nombre de jetons n'est pas connu à l'avance, cela n'est pas possible dans les scripts shell Windows standard –

+0

Exactement harpo La variable des serveurs est simple à titre d'exemple Dans la pratique (dans mon cas, ce script sera utilisé dans le cadre de le schéma de déploiement), la variable des serveurs est lue comme un paramètre utilisateur, sachant que je demande l'impossible est également bon pour que je puisse passer à des approches alternatives – bjaxbjax

4

Je ne peux pas croire cela est si compliqué! On dirait que tout le monde voudrait diviser une chaîne fréquemment sans savoir combien il y a de jetons et sans l'analyser par des lignes. En règle générale, il semble que les gens écrivent dans un fichier, puis passent à travers ou en utilisant une méthode multi sous comme ces autres. Quel bordel!

Voici ma solution. Il est plus facile de suivre et de travailler en ligne, c'est-à-dire sans sous-routine. Il est agréable de construire une liste de noms de fichiers ou autre et ensuite les parcourir sans avoir à leur écrire dans un fichier temporaire, s'assurer qu'il n'y a pas de collision d'écriture de fichier, supprimer le fichier temporaire, etc. Plus je ne sais pas comment retourner une valeur d'un sous-marin comme si c'était une fonction - je ne pense pas que vous le pouvez. Donc, vous devez continuer à écrire 2 sous-marins avec les autres réponses que je vois ici chaque fois que vous voulez faire quelque chose comme ça - l'un qui nourrit l'autre. Ma méthode vous permet de créer facilement des listes et de les parcourir autant de fois que vous le souhaitez tout au long du script.

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 
) 
) 

Sortie:

a 
b 
c 
d 
e 
f 
g 

Évidemment, vous pouvez simplement échanger l'écho avec quelque chose d'utile.

3

Ceci est une généralisation de la solution de Franci, de sorte que vous pouvez effectuer n'importe quel travail sur chaque élément de votre liste, sans avoir de copies du code d'itération de la liste.

 
: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 

Par exemple, vous pouvez l'appeler par:

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

J'ai fait quelque chose de similaire avec la réponse acceptée. – boileau

88

La commande pour un certain nombre de poignées délimiteurs par défaut.Dans ce cas, vous pouvez faire

@echo off 

set servers=127.0.0.1,192.168.0.1,10.100.0.1 

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

Si vous rencontrez un delimiter qui ne sont pas nativement pris en charge, vous pourriez faire un REPLACE pour préparer d'abord la chaîne de sorte qu'il est dans le format

@echo off 

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

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

aide Les appels récursifs ont la possibilité de manquer d'espace de pile une fois que vous avez dépassé un certain nombre d'éléments de la liste. En le testant également, le modèle de récursion semble fonctionner un peu plus lentement.

6

La difficulté est que la commande for est destinée à fonctionner sur des chaînes de texte multilignes que vous trouverez généralement dans les fichiers. La meilleure solution que j'ai trouvée à ce problème est de modifier votre chaîne pour qu'elle soit multiligne. Cette boucle for finale parcourra les éléments de la variable séparée par des espaces. Il le fait en remplaçant l'espace dans la variable de la liste par des retours à la ligne, puis en effectuant la boucle for normale.

+0

Juste pour ajouter, alors que le pour /? La documentation indique que (ensemble) est un ensemble de fichiers, cela fonctionne bien pour que ce soit quelque chose d'autre: par ex. pour% i dans (% data%) echo% je traiterai parfaitement les listes séparées par des virgules dans les données. La réponse ci-dessus est pratique si vous voulez faire un traitement plus complexe basé sur des jetons avec for/F, mais la plupart du temps faire une ligne droite est très bien. – kybernetikos

1

Oui, je sais que c'est un sujet très ancien, mais j'ai récemment découvert et intéressant truc qui peut effectuer la répartition demandée d'une manière beaucoup plus simple. Ici, il est:

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

Ces deux lignes divisent la chaîne servers dans la serverarray et aussi définir la variable n avec le nombre d'éléments créés. De cette façon, vous avez juste besoin d'utiliser une commande for /L de 1 à %n% pour traiter tous les éléments. Ici, il est le code complet:

@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]! 

Cette méthode est non seulement plus simple et plus rapide que toute autre méthode basée sur commande for, mais il permet également d'utiliser directement une chaîne de caractères multi comme séparateur. Vous pouvez lire plus de détails sur la méthode de division au this post.

0

Si PowerShell est disponible, vous pouvez:

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 

Une autre utilisation pour afficher une variable lisible par PATH.

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

ou

powershell -NoProfile -Command "$Env:PATH -split ';'" 
Questions connexes