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 diviser et traiter chaque élément de la liste?
@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
Les sorties:
In subroutine
127.0.0.1
In subroutine
ECHO is off.
pdate: 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
Les sorties:
In subroutine
127.0.0.1
In subroutine
192.168.0.1
In subroutine
10.100.0.1
pdate: 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 simple récursivité en tête de liste. (J'ai changé les adresses IP en nombres simples pour la simplicité de la liste)
Enfin, le cours LISP que j'ai suivi il y a 18 ans a porté ses fruits ...
@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
La commande for gère un certain nombre de 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 délimiteur qui n'est pas pris en charge en mode natif, vous pouvez effectuer un remplacement pour préparer d'abord la chaîne afin qu'elle soit au bon format
@echo off
set [email protected]@10.100.0.1
set servers=%servers:@=,%
for %%i in (%servers%) do (
echo %%i
)
L'utilisation d'appels récursifs a la chance de manquer d'espace sur la pile une fois que vous avez parcouru un certain nombre d'éléments dans la liste. Le testant également, le modèle de récursivité semble fonctionner un peu plus lentement.
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.
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)
Cette boucle for finale itérera sur les éléments de la variable séparée par des espaces. Il le fait en remplaçant l'espace dans la variable de liste par des sauts de ligne, puis en faisant la boucle normale.
Je ne peux pas croire que c'est si compliqué! On dirait que tout le monde voudrait séparer une chaîne fréquemment sans savoir combien de jetons elle contient et sans l'analyser par lignes. En règle générale, il semble que les gens écrivent dans un fichier, puis le parcourent ou utilisent une méthode multi-sub comme ces autres. Quel bordel!
Voici ma solution. Il est plus facile à suivre et fonctionne en ligne, c'est-à-dire sans sous-routine. Il est agréable de créer une liste de noms de fichiers ou autre chose, puis de les parcourir sans avoir à les écrire dans un fichier temporaire, assurez-vous qu'il n'y a pas de collision d'écriture de fichier, supprimez le fichier temporaire, etc. De plus, je ne sais pas comment revenir une valeur d'un sous comme c'était une fonction - je ne pense pas que vous puissiez. Il faudrait donc continuer à écrire 2 sous-marins avec les autres réponses que je vois ici chaque fois que vous vouliez 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.
Il s'agit d'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 Utilisation: appel: list_iter: do_sub_for_each_item "CSV des éléments" set foo =% 1 set list =% ~ 2 pour/f "tokens = 1 * delims =," %% i in ("% list%") do ( call% foo% %% i sinon "%% j" == "" (appel: list_iter% foo% "%% j") ) exit /b
Par exemple, vous pouvez l'appeler par:
appel: list_iter: my_foo "un, deux, trois" appel: list_iter: my_bar "Soleil, caca, pipi"
Oui, je sais que c'est un sujet très ancien, mais j'ai récemment découvert une astuce intéressante qui peut effectuer le fractionnement demandé de manière beaucoup plus simple. C'est ici:
set n=1
set "server[!n!]=%servers:,=" & set /A n+=1 & set "server[!n!]=%"
Ces deux lignes divisent la chaîne servers
en server
tablea et définissent également la variable n
avec le nombre d'éléments créés. De cette façon, il vous suffit d'utiliser un for /L
commande de 1 à %n%
pour traiter tous les éléments. Voici 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 la commande for
, mais elle permet également d'utiliser directement une chaîne à plusieurs caractères comme délimiteur.
Vous pouvez lire plus de détails sur la méthode de fractionnement sur cet article .
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 PATH lisible.
@powershell -NoProfile -Command "($Env:PATH).Split(';')"
ou
powershell -NoProfile -Command "$Env:PATH -split ';'"