将txt文件中的行批量读取到变量中

Posted

技术标签:

【中文标题】将txt文件中的行批量读取到变量中【英文标题】:Reading lines from a txt file into variables in batch 【发布时间】:2021-02-08 02:03:18 【问题描述】:

我正在尝试弄清楚如何从名为“IPList.txt”的文件中将 IP 地址读取到批处理脚本中的各个变量中。这是我目前所拥有的。

:DEFINITIONS
set LOGFILE=IPScript.log
set IPLIST=C:\IPLIST.txt
echo Script Started >> %LOGFILE%
goto SetIP

:SetIP
for /f "tokens=*" %%a in (%IPLIST%) do (
set FirstIP=%%a
)
echo The first IP is %FirstIP% >> %LOGFILE%
exit

我在“IPscript.log”中得到的输出是“第一个 IP 是:”,没有列出 IP,只是一个空格。另外,我有没有办法像这样在一个 for 循环中设置多个 IP?

【问题讨论】:

不要将某些输出重定向到文件,您应该首先查看重要部分,例如 FOR 循环。在循环中添加一个简单的echo Read: %%a,以更好地了解发生了什么 所以我这样做了,发现它正在输出文本文件中的所有5个IP。你知道是否可以将这些中的每一个都设置为一个变量? 【参考方案1】:

这里有一个可以帮助您的简单示例:

@Echo Off
SetLocal EnableExtensions DisableDelayedExpansion

:DEFINE_LOCAL_VARIABLES
Set "IPLIST=C:\IPLIST.txt"
Set "LOGFILE=IPScript.log"

:CHECK_SOURCE_EXISTS
For %%G In ("%IPLIST%") Do If "%%~aG" Lss "-" (
    Echo The file %IPLIST% does not exist.
    Echo Press any key to end this script.
    Pause 1> NUL
    GoTo :EOF
) Else If "%%~aG" GEq "d" (
    Echo Expected a file, but %IPLIST% is a directory.
    Echo Press any key to end this script.
    Pause 1> NUL
    GoTo :EOF
)

:UNDEFINE_LOCAL_VARIABLES
For /F "Delims==" %%G In ('"(Set IP[) 2> NUL"') Do Set "%%G="

:START_MAIN
Set "i=1000"
(
    Echo Script Started
    For /F UseBackQ^ Delims^=^ EOL^= %%G In ("%IPLIST%") Do (
        Set /A i += 1
        SetLocal EnableDelayedExpansion
        For %%H In ("!i:~-3!") Do (
            EndLocal
            Set "IP[%%~H]=%%G"
            Echo IP[%%~H] is %%G
        )
    )
) 1> "%LOGFILE%"

:CHECK_IP_VARIABLES_EXIST
If Not Defined IP[001] (
    Echo %IPLIST% had no readable file content.
    Echo Press any key to end this script.
    Pause 1> NUL
    GoTo :EOF
)

:VIEW_IP_VARIABLES
Set IP[

Pause & GoTo :EOF

如果您有一个现有的%LOGFILE%,并且您打算附加到它(而不是覆盖/创建一个),请将1> "%LOGFILE%" 更改为1>> "%LOGFILE%"

如果你真的不需要%LOGFILE%例如它只是你用来测试的,它看起来更像这样:

@Echo Off
SetLocal EnableExtensions DisableDelayedExpansion

:DEFINE_LOCAL_VARIABLES
Set "IPLIST=C:\IPLIST.txt"

:CHECK_SOURCE_EXISTS
For %%G In ("%IPLIST%") Do If "%%~aG" Lss "-" (
    Echo The file %IPLIST% does not exist.
    Echo Press any key to end this script.
    Pause 1> NUL
    GoTo :EOF
) Else If "%%~aG" GEq "d" (
    Echo Expected a file, but %IPLIST% is a directory.
    Echo Press any key to end this script.
    Pause 1> NUL
    GoTo :EOF
)

:UNDEFINE_LOCAL_VARIABLES
For /F "Delims==" %%G In ('"(Set IP[) 2> NUL"') Do Set "%%G="

:START_MAIN
Set "i=1000"
Echo Script Started
For /F UseBackQ^ Delims^=^ EOL^= %%G In ("%IPLIST%") Do (
    Set /A i += 1
    SetLocal EnableDelayedExpansion
    For %%H In ("!i:~-3!") Do (
        EndLocal
        Set "IP[%%~H]=%%G"
    )
)

:CHECK_IP_VARIABLES_EXIST
If Not Defined IP[001] (
    Echo %IPLIST% had no readable file content.
    Echo Press any key to end this script.
    Pause 1> NUL
    GoTo :EOF
)

:VIEW_IP_VARIABLES
Set IP[

Pause & GoTo :EOF

两个例子的最后一行都是为了显示。如果你在 cmd.exe 中测试/运行这个脚本,你可以省略它。

【讨论】:

【参考方案2】:
FOR /f "tokens=1*delims=:" %%a IN ('findstr /n /r ".*" "%filename1%"') DO set "IP%%a=%%b"
)
set IP

findstr 读取filename1 中的文件并生成格式为n:content of line n 的列表。

for /f 读取此列表,并使用 2 个标记对其进行分区 - %%a 获取第一个标记 (1),%%b 使用 : 作为分隔符获取行的其余部分 (*)。

所以只需从那里设置IP 变量。

set ip 显示以ip 开头的所有变量

可能是您的文件在最后一个 IP 之后包含空行。您的原始代码会报告 LAST IP,而不是 FIRST,因为 firstip 中的值在每次迭代时都会被覆盖,因此可以通过将其设置为 nothing 读取空行时。

在这些情况下,上面的解决方案将简单地执行(例如)set "IP6=",清除变量。

你可以通过使用获得第一个IP

if not defined firstip set "FirstIP=%%a"

我在这里假设一个干净的环境 - 也就是说,您运行的每个批处理都在 @echo off 之后包含一个 setlocal (批处理完成时恢复初始环境)并且使用的变量是已知的空的。

奖金:

set 命令更改为

set "IP%%a=%%b"&if "%%b" neq "" set "ipmax=%%a"

ipmax 设置为最后一个非空行的编号,因为%%b 是空行。

【讨论】:

【参考方案3】:

批处理文件可能有以下命令行:

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "LOGFILE=%~dp0IPScript.log"
set "IPLIST=%~dp0IPLIST.txt"
set "AddressCount=0"
echo Script started>"%LOGFILE%"

for /F "delims==" %%I in ('set IP_Address_ 2^>nul') do set "%%I="
if exist "%IPLIST%" for /F "useback delims=" %%I in ("%IPLIST%") do (
    set /A AddressCount+=1
    call set "IP_Address_%%AddressCount%%=%%I"
)

if not %AddressCount% == 0 (
    if %AddressCount% == 1 (
        echo The IP address is:
    ) else echo The IP addresses are:
    echo/
    set IP_Address_
) >>"%LOGFILE%"
endlocal

批处理文件前两个命令行定义了执行环境,意思是:

    禁用命令回显模式。 将当前命令扩展状态推送到堆栈并启用命令扩展。 将当前延迟扩展状态推送到堆栈并禁用延迟环境变量扩展。 将当前目录的路径推送到堆栈上。 将指针推送到堆栈上的当前环境变量列表并创建整个当前环境变量列表的副本以供下一步使用。

第三行和第四行定义了两个环境变量,分别是日志文件名和IP地址列表文件名,文件名完全限定。两个文件的文件路径定义为包含%~dp0 引用的批处理文件的目录路径。此路径始终以 \ 结尾,因此在将此路径与两个文件名连接时不需要额外的反斜杠。

第五行定义环境变量AddressCount,值为0

第六行在当前目录中创建日志文件并覆盖已经存在的日志文件。重定向运算符> 没有剩余空间,因为该空间将由命令 ECHO 输出,因此也作为尾随空格写入日志文件。

第一个带有选项/FFOR 命令在后台启动,其中%ComSpec% /c 是另一个命令进程,' 之间的命令行作为附加参数附加。所以在后台执行,Windows 安装到C:\Windows:

C:\Windows\System32\cmd.exe /c set IP_Address_ 2>nul

Windows 会为在后台启动的命令进程创建当前环境变量列表的副本。后台命令进程运行命令SET,输出所有环境变量,名称、等号和逐行分配给变量的字符串值,其中名称以IP_Address_开头。处理后台命令进程的 STDOUT 的输出分别由处理批处理文件的命令进程 FOR 捕获。 SET 在没有以 IP_Address_ 开头的名称定义的环境变量上输出的错误消息从句柄 STDERR 重定向到设备 NUL 以抑制此错误消息。

阅读有关Using command redirection operators 的Microsoft 文档,了解2>nul 的解释。重定向运算符 > 必须在 FOR 命令行上使用插入字符 ^ 转义,以便在 Windows 命令解释器在执行命令 FOR 之前处理此命令行时解释为文字字符> 在后台启动的单独命令进程中执行嵌入的dir 命令行。

FOR 在执行命令 SET 后启动的后台命令进程自行关闭后逐行处理捕获的输出。 FOR 总是忽略空行,因为 SET 没有输出空行,所以可以忽略它。

FOR 默认情况下会将当前行拆分为使用普通空格和水平制表符作为分隔符的子字符串。此处不需要此默认行拆分行为。选项delims== 将等号定义为字符串分隔符,用于分割= 上的行,这是变量名和变量值之间的字符。

FOR 接下来会忽略该行,如果第一个子字符串以分号开头,分号是默认的行尾字符。 SET 命令只输出以IP_Address_ 开头的行,因此在这种情况下可以保留默认的eol=;

FOR 仅将第一个子字符串分配给指定的循环变量 I,因为 tokens=1 是默认值。在这种情况下,这正是想要的行为。

所以 FOR 将一个以IP_Address_ 开头的环境变量名称分配给循环变量I,然后运行命令SET 以在当前列表中删除该环境变量处理批处理文件的命令进程的环境变量。

换句话说,第一个 FOR 用于删除在批处理文件之外偶然定义的名称以IP_Address_ 开头的所有环境变量。

下一行首先检查具有环境变量列表的文件是否存在于批处理文件的目录中。在这种情况下,FOR 再次用于处理行,但这次从指定的列表文件中逐行读取,而不是从后台命令进程的捕获输出中读取。使用 " 而不是 ' 和选项 usebackq 会有所不同。

使用选项delims= 来定义一个空的分隔符列表,导致每个不以; 开头的非空行完全分配给指定的循环变量I

对于分配给循环变量 I 的每个字符串,环境变量 AddressCount 的当前值使用由命令 SET 计算的算术表达式加一。

该值在下一个命令行中用于定义名称以IP_Address_ 开头的环境变量,并附加了当前地址计数值以及从分配给环境变量的文件中读取的行。

对于这样的任务,通常使用delayed expansion,第二个FOR循环的命令块中的第二个命令行将是:

set "IP_Address_!AddressCount!=%%I"

但上面的代码使用带有命令call 的替代方法第二次解析set "IP_Address_%%AddressCount%%=%%I",在IF条件留给FORset "IP_Address_%AddressCount%=%I" /strong> 被处决了。

下一个 IF 条件检查是否从列表文件中读取了包含 IP 地址的任何行。在这种情况下,首先输出一条信息行,这取决于是否从文件中读取了一行或多行。然后输出一个空行,最后是名称以IP_Address_= 开头的所有环境变量以及分配给环境变量的行(IP 地址)。所有这些输出都附加到日志文件中。

最后一条命令恢复之前的执行环境,意思是:

    丢弃当前的环境变量列表并从堆栈中弹出指向初始环境变量列表的指针,从而恢复初始环境变量列表。换句话说,在第二个命令行中的命令 SETLOCAL 之后由批处理文件定义或修改的所有环境变量都将永远丢失。 从堆栈中弹出当前目录的路径,并使该目录再次成为当前目录。 setlocalendlocal 之间的当前目录没有被它们之间的代码更改,所以这在这里无关紧要。 从堆栈中弹出延迟扩展状态并相应地启用或禁用延迟环境变量扩展以恢复初始延迟扩展行为。 从堆栈中弹出当前命令扩展状态并相应地启用或禁用命令扩展以恢复初始命令扩展行为。

要了解所使用的命令及其工作原理,请打开command prompt 窗口,在其中执行以下命令,并仔细阅读每个命令显示的所有帮助页面。

call /? echo /? endlocal /? for /? if /? set /? setlocal /?

另见:

Variables are not behaving as expected How does the Windows Command Interpreter (CMD.EXE) parse scripts? Microsoft 的 Windows Commands 文档 SS64.com - A-Z index of Windows CMD commands

【讨论】:

以上是关于将txt文件中的行批量读取到变量中的主要内容,如果未能解决你的问题,请参考以下文章

用c语言怎么读取txt文件中的行数

怎样批量提取视频的文件名?

将 .txt 文件读取到 R 中的选项卡

matlab如何批量读取带有编号的文件

编写一个对txt文件中的行进行编号的函数,跳过空白行并将输出写入另一个文件

python批量读取txt某列,并复制对应txt文件名?