批处理文件可能有以下命令行:
@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 输出,因此也作为尾随空格写入日志文件。
第一个带有选项/F 的FOR 命令在后台启动,其中%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 之后由批处理文件定义或修改的所有环境变量都将永远丢失。
- 从堆栈中弹出当前目录的路径,并使该目录再次成为当前目录。
setlocal 和 endlocal 之间的当前目录没有被它们之间的代码更改,所以这在这里无关紧要。
- 从堆栈中弹出延迟扩展状态并相应地启用或禁用延迟环境变量扩展以恢复初始延迟扩展行为。
- 从堆栈中弹出当前命令扩展状态并相应地启用或禁用命令扩展以恢复初始命令扩展行为。
要了解所使用的命令及其工作原理,请打开command prompt 窗口,在其中执行以下命令,并仔细阅读每个命令显示的所有帮助页面。
call /?
echo /?
endlocal /?
for /?
if /?
set /?
setlocal /?
另见: