dos批处理迭代分隔的字符串

时间:2010-03-26 16:32:17

标签: command-line batch-file

我有一份我想单独处理的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.

更新 使用Franci的答案作为参考,这是解决方案:

@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

7 个答案:

答案 0 :(得分:93)

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 servers=127.0.0.1@192.168.0.1@10.100.0.1
set servers=%servers:@=,%

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

一旦越过列表中的特定数量的项目,使用递归调用就有可能耗尽堆栈空间。同样测试它,递归模式似乎运行得慢一些。

答案 1 :(得分:27)

更新:如果列表中的项目数目未知,则仍可以使用列表头部的简单递归来解析所有项目。 (为了简化列表,我已将IP更改为简单数字)

最后,我在18年前获得的Lisp课程得到了回报......

@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 :(得分:7)

难点在于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)

这个最终的for循环将迭代空格分隔变量中的项。它是通过用换行符替换列表变量中的空格,然后执行正常的循环来实现的。

答案 3 :(得分:4)

我简直不敢相信这太复杂了!似乎每个人都想要频繁地分割字符串而不知道其中有多少个标记并且没有按行解析它。通常情况下,似乎人们正在写入文件,然后逐步执行它或使用像其他人一样的多子方法。真糟糕!

这是我的解决方案。它更容易跟随和内联工作,即没有子程序。很高兴建立一个文件名列表或其他什么然后逐步执行它们而不必将它们写入临时文件,确保没有文件写入冲突,删除临时文件等。另外我不知道如何返回来自sub的值就像它是一个函数 - 我认为你不能。因此,每次你想做这样的事情时,你必须继续用其他答案写下2个潜艇 - 一个用另一个来喂养。我的方法可以让您轻松地创建列表,并在整个脚本中多次遍历它们。

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

显然你可以用有用的东西来交换回声。

答案 4 :(得分: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"

答案 5 :(得分:1)

是的,我知道这是一个非常古老的话题,但我最近发现了一个有趣的技巧,可以用更简单的方式执行请求的分割。这是:

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

这两行将servers字符串拆分为server array,并使用已创建元素的数量定义n变量。这样,您只需要使用从{1}到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]! 命令的任何其他方法更简单,更快,而且还允许直接使用多字符字符串作为分隔符。

您可以在this post阅读有关拆分方法的更多详细信息。

答案 6 :(得分: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 ';'"