2010-03-26 2 views
35

У меня есть список разделенных IP-адресов, которые я хотел бы обрабатывать отдельно. Длина списка неизвестна заранее. Как разделить и обработать каждый элемент в списке?dos batch итерации через строку с разделителями

@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 
+0

Хотя рекурсивное решение хорошо, я думаю, что замена один символ новой строки ниже проще. – kybernetikos

ответ

26

Обновление: Если количество элементов в списке неизвестно, все равно можно проанализировать все элементы с простой рекурсией в начале списка. (Я изменил IP-адрес для простых чисел для простоты списка)

Наконец, что Лисп класса я взял 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 
+1

Это не работает - он просто печатает «b» и «c» после первого IP-адреса. Кроме того, ОП заявила, что количество входов будет неизвестно раньше времени. Это может быть невозможно в чистом файле DOS - но я рад узнать, что я не единственный, кто все еще их использует! – harpo

+0

моя ошибка, забыл изменить * на номера токенов. И вы правы - если количество токенов неизвестно заранее, это невозможно в стандартных сценариях оболочки Windows. –

+0

Точно harpo. Переменная серверов служит просто в качестве примера. На практике (в моем случае этот скрипт будет использоваться как часть схема развертывания), переменная серверов считывается как пользовательский параметр. Знание, что я прошу невозможное, также хорошо, поэтому я могу перейти к альтернативным подходам. – bjaxbjax

4

Я не могу поверить в это так сложно! Похоже, все хотели бы разбить строку часто, не зная, сколько жетонов в ней, и не разбирать ее по строкам. Как правило, кажется, что люди пишут в файл, а затем проходят через него или используют метод нескольких субпоследовательностей, подобных этим другим. Какой беспорядок!

Вот мое решение. Легче следить и работать внутри, т. Е. Нет подпрограммы. Приятно создавать список имен файлов и т. Д., А затем проходить через них, не записывая их в временный файл, убедитесь, что нет столкновений с файлами, удалите временный файл и т. Д. Кроме того, я не знаю, как вернуть значение от sub, как это было функцией - я не думаю, что вы можете. Таким образом, вам придется писать две подписи с другими ответами, которые я вижу здесь каждый раз, когда вы хотели сделать что-то вроде этого - одно, которое передает другой. Мой метод позволяет вам легко создавать списки и проходить через них столько раз, сколько вы хотите по всему сценарию.

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 

Очевидно, что вы можете просто обмениваться эхо с чем-то полезным.

3

Это обобщение решения 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" 
+0

Я сделал что-то подобное этому с принятым ответом. – boileau

88

для команды обрабатывает несколько разделителей по умолчанию.В этом случае вы можете сделать

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

Использование рекурсивные вызовы имеют шанс вырваться из пространства стека, когда вы переходите через определенное количество элементов в списке. Также, тестируя его, рекурсивный шаблон работает немного медленнее.

6

Сложность состоит в том, что команда 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) 

Этот конечный цикл цикла будет перебирать элементы в переменной, разделенной пробелом. Он делает это, заменяя пространство в переменной списка на новые строки, а затем выполняя нормальный цикл.

+0

Просто добавьте, в то время как для /? документация говорит, что (набор) представляет собой набор файлов, он отлично работает, чтобы быть чем-то другим: например. for% i in (% data%) do echo% Я отлично обрабатываю списки, разделенные запятыми, в данных. Ответ выше удобен, если вы хотите сделать более сложную обработку на основе токенов с помощью/F, но большую часть времени делать прямо. – kybernetikos

1

Да, я знаю, что это очень старая тема, но я недавно обнаружил и интересный трюк, который может выполнить запрошенный раскол намного проще. Вот оно:

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

Эти две линии разделения servers строку в serverarray, а также определить 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 команды, но она также позволяет напрямую использовать строку нескольких символов в качестве разделителя.

Подробную информацию о методе расщепления можно получить по адресу this post.

0

Если 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 ';'" 
Смежные вопросы