在第二个 if 语句中,我想从最终的文件名列表中过滤掉“tool”或“tool.bat”。然而,最终的文件名列表包括“tool”并且total_bags正在增加。我想知道我做错了什么导致程序无法捕获这种情况。
set /A total_bags=0
set target=%~1
if "%target%"=="" set target=%cd%
set LF=^
rem Previous two lines deliberately left blank for LF to work.
for /f "tokens=1 delims=. " %%i in ('dir /b /s /a:-d "%target%"') do (
set current_file=%%~ni
echo !unique_files! | find "!current_file!:" > nul
if NOT !ERRORLEVEL! == 0 (
if NOT !current_file! == "tool.bat" (
set /A total_bags=total_bags+1
set unique_files=!unique_files!!current_file!:
)
)
)
echo %unique_files::=!LF!%
echo %total_bags%
endlocal
最初使用的条件
if NOT "%current_file%" == "tool.bat"
不起作用,因为 %current_file%
已被环境变量 current_file
的当前字符串替换,Windows 命令处理器上的空字符串正在处理以 (
开头的整个命令块,并且以匹配的 )
before 执行命令 FOR 结束。这可以在调试批处理文件中看到。另请参阅 变量未按预期运行,了解一个非常好的简短示例,解释 Windows 命令解释器 (CMD.EXE) 如何解析脚本。
通常不建议将已分配给循环变量的字符串分配给未在 FOR 循环内进一步修改的环境变量。最好在代码中需要引用当前文件名的地方使用
%%~ni
。
使用延迟扩展需要使用
setlocal EnableDelayedExpansion
(或使用setlocal EnableExtensions EnableDelayedExpansion
来显式启用默认情况下启用的命令扩展)来启用它,因为与命令扩展相比,它默认情况下未启用。然后,Windows 命令处理器第二次解析每个命令行,并在执行命令 IF时展开
!current_file!
。
但是,对于名称为
if NOT !current_file! == "tool.bat"
的批处理文件,即使 tool.bat
也始终评估为 true,因为 set current_file=%%~ni
会导致仅将字符串 current_file
(不带文件扩展名的文件名)和左侧分配给环境变量 tool
字符串不包含在双引号中,而正确的字符串始终包含在双引号中。在比较两个字符串之前,命令 IF 不会删除右侧字符串中的双引号。
有问题的批处理文件还错过了
FOR循环上方的
set unique_files=
,以显式取消定义环境变量 unique_files
,以防在启动批处理文件时偶然已定义(例如,在命令提示符中之前执行过的情况)窗户。
所讨论的批处理文件的另一个问题是变量名+等号+分配给环境变量的字符串的最大字符串长度是8191个字符,这是一个问题,因为数千个文件名被连接到分配给一个环境的长字符串变量如
unique_files
.
我建议使用这个带有注释的批处理文件来解释它。
@echo off
setlocal EnableExtensions DisableDelayedExpansion
rem Delete all environment variables of which name starts very unusual
rem with a question mark existing already by chance (with exception of
rem those environment variables with multiple question marks in name).
for /F "delims=?" %%I in ('set ? 2^>nul') do set "?%%I?="
rem Search with the string passed as first argument or simply within current
rem directory recursively for all files and define for each file name an
rem environment variable with a question mark at beginning and one more at
rem end of the variable name. A file name cannot contain a question mark.
rem The value assigned to the environment variable does not matter. As it
rem is not possible to define multiple environment variables with same name
rem and environment variable names are case-insensitive, there is just one
rem environment variable defined on multiple files have same file name.
rem The batch file itself is ignored because of the IF condition.
for /F "delims=" %%I in ('dir "%~1" /A-D /B /S 2^>nul') do if not "%%I" == "%~f0" set "?%%~nI?=1"
rem Initialize the file counting environment variable.
set "FileCount=0"
rem Output all file names which are the environment variable names sorted
rem alphabetically with the question marks removed and additionally count
rem the number of file names output by this loop.
for /F "eol=| delims=?" %%I in ('set ? 2^>nul') do set /A "FileCount+=1" & echo %%I
rem Output finally the number of unique file names excluding file extensions.
echo %FileCount%
rem Restore initial execution environment which results also in the
rem deletion of all environment variables defined during batch execution.
endlocal
它不使用延迟扩展,因此也适用于文件名中包含一个或多个
!
的文件名,由于感叹号,在启用在线set current_file=%%~ni
上的延迟扩展时会被错误处理文件名中的内容将被解释为延迟扩展环境变量引用的开始/结束。
为每个唯一的文件名定义了一个环境变量。环境变量的数量仅受环境变量可用内存总量的限制,即 64 MiB。即使对于目录树中数千个唯一的文件名来说,这也应该足够了。
要了解所使用的命令及其工作原理,请打开命令提示符窗口,执行以下命令,并完整、仔细地阅读每个命令显示的帮助页面。
call /?
...解释 %~f0
引用参数 0 的全名,即当前处理的批处理文件的完整限定文件名,并且 %~1
引用第一个参数,并从参数字符串中删除可能现有的周围 "
。 dir /?
echo /?
endlocal /?
for /?
if /?
rem /?
set /?
setlocal /?
阅读有关 使用命令重定向运算符的 Microsoft 文档,了解
2>nul
的说明。当 Windows 命令解释器在执行命令 FOR(执行嵌入的
>
或^
命令行使用单独的命令进程在后台启动,dir
和set
中的命令行作为附加参数附加。