web-dev-qa-db-fra.com

dos batch itère à travers une chaîne délimitée

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
36
bjaxbjax

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
28
Franci Penov

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.

94
Matthew Benedict

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.

7
kybernetikos

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.

4
Jonathan

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" 
3
azhrei

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 servertablea 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 .

1
Aacini

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 ';'"
0
lit