Windows 为什么批处理脚本不能正确解释字符串

Windows 为什么批处理脚本不能正确解释字符串,windows,batch-file,cmd,Windows,Batch File,Cmd,在我的第二个if语句中,我想从最终的文件名列表中过滤掉“tool”或“tool.bat”。但是,文件名的最终列表包括“工具”,并且文件包总数正在增加。我想知道我做错了什么导致程序无法捕捉到这个案例 set /A total_bags=0 set target=%~1 if "%target%"=="" set target=%cd% set LF=^ rem Previous two lines deliberately left blank for

在我的第二个if语句中,我想从最终的文件名列表中过滤掉“tool”或“tool.bat”。但是,文件名的最终列表包括“工具”,并且文件包总数正在增加。我想知道我做错了什么导致程序无法捕捉到这个案例

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

最初使用的条件
如果不是“%current\u file%”==“tool.bat”
不起作用,因为
%current\u file%
已被环境变量
current\u file
的当前字符串分别替换。Windows命令处理器上的空字符串正在处理从
开始的整个命令块(
并以匹配的
结尾)
在执行的命令之前。可以在上看到。另请参阅,以获得一个非常好的简短示例说明

通常不建议将已分配给循环变量的字符串分配给环境变量,该环境变量不会在FOR循环中进一步修改。最好在需要引用当前文件名的代码中的任何地方使用
%%~ni

使用需要使用
setlocal EnableDelayedExpansion
(或使用
setlocal EnableDelayedExpansion EnableDelayedExpansion
来显式启用默认启用的命令扩展)与命令扩展名相比,默认情况下它未启用。然后Windows命令处理器再次解析每个命令行,并在执行命令时展开
!current_file!

但是,即使是
如果不是!current_file!==“tool.bat”
对名为
tool.bat
的批处理文件的计算结果始终为true,因为
设置current_file=%%~ni
会导致分配给环境变量
current_file
的仅是字符串
tool
(文件名,无文件扩展名)左字符串不包含在双引号中,而右字符串始终包含在双引号中。在比较两个字符串之前,IF
命令不会删除右字符串中的双引号

如果在启动批处理文件时偶然定义了环境变量
unique\u files
,例如从命令提示窗口中的上一次执行,则所讨论的批处理文件也会错过
set unique\u files=

有关批处理文件的另一个问题是,分配给环境变量的变量名+等号+字符串的最大字符串长度为8191个字符,这是数千个文件名连接到分配给一个环境变量的长字符串(如
unique\u 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
它不使用延迟扩展,因此也适用于文件名中包含一个或多个
的文件名,如果在第
行启用延迟扩展,则会因为惊叹号而处理错误
设置当前文件=%%~ni
in文件名将被解释为延迟扩展环境变量引用的开始/结束

为每个唯一的文件名定义了一个环境变量。环境变量的数量仅受环境变量的总可用内存(64 MiB)的限制。即使对于目录树中数千个唯一的文件名,这也应该足够了

要了解所使用的命令及其工作方式,请打开一个窗口,在其中执行以下命令,并非常仔细地阅读为每个命令显示的所有帮助页面

  • call/?
    …解释了引用参数0全名的
    %~f0
    ,该参数是当前处理的批处理文件的完整限定文件名,
    %~1
    引用的第一个参数可能已从参数字符串中删除
  • dir/?
  • echo/?
  • endlocal/?
  • 获取/?
  • 如果/?
  • rem/?
  • 设置/?
  • setlocal/?

阅读有关的Microsoft文档,以了解有关
2>nul
的解释。在执行命令之前,Windows命令解释器处理此命令行时,必须使用上的插入符号
^
对重定向运算符
进行转义,才能将命令行解释为文字字符FOR在一个单独的命令过程中执行嵌入的
dir
set
命令行,该命令过程在后台启动,并将
%ComSpec%/c
中的命令行作为附加参数附加。
我正在处理的文件系统的一部分使用两个扩展名,例如filename.extension1.extension2.我尝试运行您的解决方案,但它只删除了最右边的扩展名。我如何删除这两个扩展名?没有两个扩展名。文件扩展名从文件的最后一个点到文件名的末尾都是。在Linux上,文件名以点开头就隐藏了。例如
。htaccess
是文件htaccess在Linux上被隐藏。该文件
.htaccess
适用于Windows,是一个没有文件名且文件扩展名为
.htaccess
的文件。您可以将
设置“%%nI”=1”
替换为“%%J in”(%%nI”)的
是否设置“%%nJ”=1“
从每个文件名中删除两次从文件名最后一个点到最后一个点的字符串,这对只有一个点和后面的某个点的文件没有区别。