批量阅读文件夹时有奇怪的行为

我试图做一个简单的批处理(这不是整个事情,但这是使一切都失败的部分)

@echo off for /f "tokens=*" %%G in ('dir /s /b /a:d "e:\tmp\*"') do ( echo %%G set fullpath=%%G set basename=%fullpath:~7% echo %fullpath% echo %basename% ) 

这个剧本应该是从任何地方运行的,因此这个剧本有趣的循环。 它应该查看目录,然后做一些东西。

在这个特定的目录中,还有另外3个目录: bomslenovodbcpatfinance

预期的产出

 e:\tmp\bomslenovodb e:\tmp\bomslenovodb bomslenovodb e:\tmp\cpat e:\tmp\cpat cpat e:\tmp\finance e:\tmp\finance finance 

实际输出

 First run e:\tmp\bomslenovodb ECHO is off. ECHO is off. e:\tmp\bomslenovodb e:\tmp\cpat ECHO is off. ECHO is off. e:\tmp\cpat e:\tmp\finance Second run ECHO is off. ECHO is off. e:\tmp\finance e:\tmp\bomslenovodb e:\tmp\finance ECHO is off. e:\tmp\bomslenovodb e:\tmp\cpat e:\tmp\finance Third run ECHO is off. e:\tmp\cpat e:\tmp\finance e:\tmp\finance ECHO is off. e:\tmp\finance e:\tmp\bomslenovodb e:\tmp\finance e:\tmp\finance Fourth run e:\tmp\bomslenovodb e:\tmp\cpat e:\tmp\finance e:\tmp\finance e:\tmp\cpat e:\tmp\finance e:\tmp\finance e:\tmp\finance e:\tmp\finance 

在我看来, set fullpath=%%G是不符合预期的,所以它的值没有正确设置。

我在Windows Server 2008的机器,任何想法?

经典批次错误:-)

SET命令工作正常。 这是你的扩张失败。

parsing语句时发生%VAR%扩展,并且FOR循环中的所有命令都一次被parsing。 任何括号的代码块也是如此。 因此,在执行FOR循环的过程中, %fullpath%%basename%是不变的 – input循环之前存在的值(在这种情况下是未定义的)。

解决方法是使用延迟扩展,即在命令执行之前发生。 在使用setlocal enableDelayedExpansion之前,必须启用延迟扩展。 扩展的语法变为!VAR!

 @echo off setlocal enableDelayedExpansion for /f "tokens=*" %%G in ('dir /s /b /a:d "e:\tmp\*"') do ( echo %%G set fullpath=%%G set basename=!fullpath:~7! echo !fullpath! echo !basename! ) 

但是还有一个潜在的问题。 文件名可以包含! 字符和任何FORvariables包含! 当延迟扩展被启用时,将在扩展时被破坏。 解决scheme是在循环内切换延迟扩展和closures。

 @echo off for /f "tokens=*" %%G in ('dir /s /b /a:d "e:\tmp\*"') do ( echo %%G set fullpath=%%G setlocal enableDelayedExpansion set basename=!fullpath:~7! echo !fullpath! echo !basename! endlocal ) 

如果你需要保护! 文字,你需要你的variables分配来持续迭代,那么最简单的事情就是使用一个CALLed过程,这样你就可以使用正常的扩展。 只需将FORvariables值转换为CALL参数即可。 但是使用CALL比将所有东西直接放入循环要慢很多。

 @echo off for /f "tokens=*" %%G in ('dir /s /b /a:d "e:\tmp\*"') do call :proc "%%G" exit /b :proc echo %~1 set "fullpath=%~1" set "basename=%fullpath:~7%" echo %fullpath% echo %basename% exit /b