Dos итерации через строку с разделителями
У меня есть разделенный список IP-адресов, которые я хотел бы обрабатывать отдельно. Длина списка неизвестна заранее. Как разделить и обработать каждый элемент в списке?
@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
Выходы:
In subroutine
127.0.0.1
In subroutine
ECHO is off.
Update:
Используя Францию в качестве ссылки, здесь решение:
@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
Выходы:
In subroutine
127.0.0.1
In subroutine
192.168.0.1
In subroutine
10.100.0.1
Ответы
Ответ 1
Обновить. Если количество элементов в списке неизвестно, все равно можно проанализировать все элементы с простой рекурсией в заголовке списка. (Я просто изменил IP-адреса на простые номера для простоты списка)
Наконец, что класс Lisp, который я взял 18 лет назад, окупился...
@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
Ответ 2
Команда for обрабатывает по умолчанию несколько разделителей. В этом случае вы можете сделать
@echo off
set servers=127.0.0.1,192.168.0.1,10.100.0.1
for %%i in (%servers%) do (
echo %%i
)
Если вы запустили разделитель, который не поддерживается изначально, вы можете сделать замену, чтобы сначала подготовить строку так, чтобы она находилась в правильном формате
@echo off
set [email protected]@10.100.0.1
set servers=%servers:@=,%
for %%i in (%servers%) do (
echo %%i
)
Использование рекурсивных вызовов имеет шанс вырваться из пространства стека, когда вы переходите через определенное количество элементов в списке. Также, тестируя его, образец рекурсии работает немного медленнее.
Ответ 3
Трудность состоит в том, что команда for предназначена для работы с многострочными текстовыми строками, которые вы обычно находите в файлах. Лучшее решение, которое я нашел для этой проблемы, состоит в том, чтобы изменить вашу строку как многострочную.
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)
Этот цикл final для цикла будет перебирать элементы в переменной, разделенной пробелом. Он делает это, заменяя пространство в переменной списка символами новой строки, а затем выполняя нормальный цикл.
Ответ 4
Не могу поверить, что это так сложно! Похоже, все хотели бы разбить строку часто, не зная, сколько жетонов в ней, и не разбирать ее по строкам. Как правило, кажется, что люди пишут в файл, а затем проходят через него или используют метод нескольких субпоследовательностей, подобных этим другим. Какой беспорядок!
Вот мое решение. Легче следить и работать в строгом режиме, то есть нет подпрограммы. Приятно создавать список имен файлов и т.д., А затем проходить через них, не записывая их в временный файл, убедитесь, что нет столкновений с файлами, удалите временный файл и т.д. Кроме того, я не знаю, как вернуть значение от sub, как это было функцией - я не думаю, что вы можете. Таким образом, вам придется писать две подписи с другими ответами, которые я вижу здесь каждый раз, когда вы хотели сделать что-то вроде этого - одно, которое передает другой. Мой метод позволяет вам легко создавать списки и проходить через них столько раз, сколько требуется на протяжении 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
)
)
Вывод:
a
b
c
d
e
f
g
Очевидно, вы можете просто обменять эхо с чем-то полезным.
Ответ 5
Это обобщение решения Franci, так что вы можете выполнять любое задание по каждому элементу в своем списке, не имея копии итерационного кода списка.
: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
Например, вы можете вызвать его:
call :list_iter :my_foo "one,two,three"
call :list_iter :my_bar "sun,poo,wee"
Ответ 6
Да, я знаю, что это очень старая тема, но я недавно обнаружил и интересный трюк, который может выполнить запрошенный раскол намного проще. Вот он:
set n=1
set "server[!n!]=%servers:,=" & set /A n+=1 & set "server[!n!]=%"
Эти две строки разбивают строку servers
на server
array, а также определяют переменную n
с количеством созданных элементов. Таким образом вам просто нужно использовать команду for /L
от 1 до %n%
для обработки всех элементов. Вот полный код:
@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]!
Этот метод не просто проще и быстрее, чем любой другой метод, основанный на команде for
, но также позволяет напрямую использовать многосимвольную строку в качестве разделителя.
Вы можете прочитать дополнительную информацию о методе разделения этот пост.
Ответ 7
Если PowerShell доступен, вы можете:
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
Другое использование для отображения читаемой переменной PATH.
@powershell -NoProfile -Command "($Env:PATH).Split(';')"
или
powershell -NoProfile -Command "$Env:PATH -split ';'"