如何使用Windows批处理文件循环遍历文本文件中的每一行?

294

我希望知道如何使用Windows批处理文件逐行遍历文本文件,并依次处理每一行的文本。

12个回答

374

我需要将整行作为一个整体进行处理。这是我发现可行的方法。

for /F "tokens=*" %%A in (myfile.txt) do [process] %%A

在整行文本中使用带星号(*)的tokens关键字会提取所有文本。如果没有加入星号,它只会提取该行的第一个单词。我猜这与空格有关。

有关技术网上的命令


如果您的文件路径中有空格,则需要使用usebackq。例如:

for /F "usebackq tokens=*" %%A in ("my file.txt") do [process] %%A

47
一个小的修改:为了让这个命令行交互工作,请在上面的命令中将%%A替换为%A。否则你会得到%%A was unexpected at this time.的错误提示。 - vadipp
25
如果您需要执行多行命令,在“DO”后面可以加上一个开放的括号“(”,几行后用一个闭括号“)”结束 - 您可以将代码块放在这些括号中(根据您的喜好进行缩进)。 - BrainSlugs83
6
谢谢提供这个命令。我发现在文件名中加上引号(")会出问题——对于带有空格的文件名只会输出文件名本身。例如,for /F "tokens=*" %%A in ("myfile.txt") do echo A = %%A 输出为 A = myfile.txt。你有什么办法解决这个问题吗? - will
2
确保你正在处理的文件是ANSI或UTF8编码。我一直在纳闷为什么这不起作用,直到我尝试使用TYPE命令查看文件并发现输出结果与我的预期不同。此时我注意到该文件以某种原因被编码为“UCS-2 BE BOM”! - Dan Stevens
4
值得指出的是,在你的循环中,索引参数必须是一个单个字符。因此,例如%%i是可以的,但%%index会失败。 - Vincent
显示剩余8条评论

63

来自Windows命令行参考:

要解析一个文件,忽略注释行,请输入:

for /F "eol=; tokens=2,3* delims=," %i in (myfile.txt) do @echo %i %j %k

该命令解析Myfile.txt中的每一行,忽略以分号开头的行,并将每行的第二个和第三个标记传递给FOR语句主体(标记由逗号或空格分隔)。 FOR语句的主体引用%i获取第二个标记,%j获取第三个标记,%k获取所有剩余的标记。

如果您提供的文件名包含空格,请在文本周围使用引号(例如,“文件名”)。要使用引号,请使用usebackq。否则,引号将被解释为定义要解析的文字字符串。

顺便说一下,在大多数Windows系统上,您可以在以下位置找到命令行帮助文件:

 "C:\WINDOWS\Help\ntcmds.chm"

9
为了“澄清”这个句子:“要使用引号,你必须使用 usebackq”,它的意思是,在 for 命令中访问包含空格的路径时,需要在 for /f 语句中添加 "usebackq" 参数。具体示例如下:for /f "usebackq" %%a in ("Z:\My Path Contains Spaces\xyz\abc.txt") - drzaus
你为什么省略了第一个标记? - Victorio Berra

38

在批处理文件中,您必须使用%%而不是%:(键入help for

for /F "tokens=1,2,3" %%i in (myfile.txt) do call :process %%i %%j %%k
goto thenextstep
:process
set VAR1=%1
set VAR2=%2
set VAR3=%3
COMMANDS TO PROCESS INFORMATION
goto :EOF

这段代码的作用是: 在for命令的结尾处运行"do call :process %%i %%j %%k",将myfile.txt中获取的信息传递给"process"子程序。

当您在批处理程序中使用for命令时,需要对变量使用双重百分号。

以下几行代码将这些变量从for命令传递到process 'subroutine',使您能够处理这些信息。

set VAR1=%1
 set VAR2=%2
 set VAR3=%3

如果需要更多的例子,我可以分享一些这种精密设置的高级用法。当然,根据需要加入你的EOL或分隔符。


34

改进第一个 "FOR /F.." 的答案: 我所需要做的是调用 MyList.txt 中列出的每个脚本,这样对我有用:

for /F "tokens=*" %A in  (MyList.txt) do CALL %A ARG1

--或者,如果您希望在多行上执行此操作:

for /F "tokens=*" %A in  (MuList.txt) do (
ECHO Processing %A....
CALL %A ARG1
)

编辑:上面给出的示例是用命令提示符执行FOR循环的;从批处理脚本中执行,需要添加一个额外的%,如下所示:

---START of MyScript.bat---
@echo off
for /F "tokens=*" %%A in  ( MyList.TXT) do  (
   ECHO Processing %%A.... 
   CALL %%A ARG1 
)
@echo on
;---END of MyScript.bat---

23

@MrKraus的答案非常有指导性。此外,让我补充一点,如果你想要加载与批处理文件位于同一目录中的文件,请在文件名前加上%~dp0。这里是一个例子:

cd /d %~dp0
for /F "tokens=*" %%A in (myfile.txt) do [process] %%A

NB: 如果您的文件名或目录(例如上面的示例中的myfile.txt)包含空格(例如'my file.txt'或'c:\Program Files'),请使用:

for /F "tokens=*" %%A in ('type "my file.txt"') do [process] %%A

使用type关键字调用type程序,显示文本文件的内容。如果您不想承受调用type命令的开销,应将目录更改为文本文件所在的目录。请注意,对于具有空格的文件名仍需要使用type。

我希望这能帮助到某人!


无需为文件名加前缀,因为批处理文件默认会在当前文件夹中查找。 - foxidrive
1
@foxidrive:好的,我知道了。不过还是需要注意一下。比如说,如果一个目录被改变了,那么它就会去查找那个目录,而非批处理文件所在的目录。解决方法就是在for循环之前调用“cd /d %~dp0”,这样可以确保你引用的是批处理文件所在的目录中的文件。感谢你的观察。 - Marvin Thobejane
3
谢谢您的 type 避免方案,并点赞 +1。 - halex
1
@scragar,你有正确的引号吗?它需要是一个'而不是`。在我的键盘上,它与@键位于同一键上。 - FrinkTheBrave
在Windows 10中,默认情况下批处理文件不会查找当前文件夹。相反,它会查找注册在Windows注册表中的文件夹。默认文件夹是用户主目录。 - Zimba
显示剩余3条评论

20

这个被接受的答案很好,但有两个限制。
它会删除空行和以 ; 开头的行。

要读取任何内容的行,您需要使用延迟扩展切换技术。

@echo off
SETLOCAL DisableDelayedExpansion
FOR /F "usebackq delims=" %%a in (`"findstr /n ^^ text.txt"`) do (
    set "var=%%a"
    SETLOCAL EnableDelayedExpansion
    set "var=!var:*:=!"
    echo(!var!
    ENDLOCAL
)

使用findstr命令在每一行前加上行号和冒号,这样空行就不再为空了。

访问%%a参数时需要禁用DelayedExpansion,否则感叹号!和插入符号^会丢失,因为它们在该模式下具有特殊含义。

但是,要删除行中的行号,需要启用延迟扩展。
set "var=!var:*:=!"将删除所有内容直到第一个冒号(使用delims=:会删除行首的所有冒号,而不仅仅是来自findstr的冒号)。
endlocal指令再次禁用延迟扩展,以供下一行使用。

现在唯一的限制是行长约为8191个字符,但似乎没有办法克服这个限制。


Win 10不允许在命令行上使用setlocal。当我在CMD上运行代码时,我得到的是!var!而不是空格。如何解决? - Zimba
行长限制可以通过将文件分割为最大行长8190的临时文件进行处理来克服。然后重新组合成一个文件。 - Zimba
Zimba:- 我认为你想创建一个批处理文件并粘贴整个代码片段。我相信你已经弄清楚了。但是,这可能会帮助下一个人。Jeb:- echo行当前读取:- echo (!var!我认为它应该读取:-echo !var!我不确定为什么我们需要发出额外的(干得好。感谢像我这样的菜鸟将其放置在公共领域。 - Daniel Adeniji
1
@DanielAdeniji echo(!var! 是正确的(没有空格),它避免了 var 中出现 ONOFF/? 等内容时出现问题。另请参阅:ECHO. FAILS to give text or blank line - Instead use ECHO/ - jeb
1
@jeb. 谢谢您的修正。 - Daniel Adeniji

14

或者,您可以排除引号中的选项:

FOR /F %%i IN (myfile.txt) DO ECHO %%i

1
在命令中,相邻的两个百分号 %% 被视为单个百分号(而不是批处理文件)。 - Paul

11
这是我编写的一个批处理文件,用于执行文件夹中的所有 SQL 脚本:
REM ******************************************************************
REM Runs all *.sql scripts sorted by filename in the current folder.
REM To use integrated auth change -U <user> -P <password> to -E
REM ******************************************************************

dir /B /O:n *.sql > RunSqlScripts.tmp
for /F %%A in (RunSqlScripts.tmp) do osql -S (local) -d DEFAULT_DATABASE_NAME -U USERNAME_GOES_HERE -P PASSWORD_GOES_HERE -i %%A
del RunSqlScripts.tmp

你可以通过让for循环处理dir命令来摆脱临时文件。for /F %%A in ('dir /B /O:n *.sql') do osql...。请注意dir命令周围的单引号。 - JMichael

7
接受的答案使用cmd.exe
for /F "tokens=*" %F in (file.txt) do whatever "%F" ...

它只适用于“普通”文件。对于大型文件,它将表现不佳。

对于大文件,您可能需要使用Powershell和类似以下的内容:

[IO.File]::ReadLines("file.txt") | ForEach-Object { whatever "$_" }

或者如果您有足够的内存:

foreach($line in [System.IO.File]::ReadLines("file.txt")) { whatever "$line" } 

这个方法对我有效,使用了一个250 MB大小的文件,其中包含超过2百万行。而for /F ...命令在几千行后就卡住了。
关于foreachForEach-Object之间的区别,请参见“了解ForEach和ForEach-Object”
(来源:在PowerShell中逐行读取文件

7
如果您使用的是NT系列的Windows操作系统(以cmd.exe为shell的操作系统),可以尝试使用FOR /F命令

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接