批量读取文件夹时出现奇怪的行为

批量读取文件夹时出现奇怪的行为

我正在尝试做一个简单的批处理(它不是全部,但这是导致一切失败的部分)

@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%
)

这个脚本应该可以从任何地方运行,因此有有趣的 for 循环。它应该查看目录,然后执行一些操作。

在这个特定的目录中,还有另外 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 机器上,有什么想法吗?

答案1

典型的批次错误:-)

SET 命令运行正常。失败的是您的扩展。

%VAR%扩展发生在语句被解析时,并且 FOR 循环内的所有命令都会被一次性解析。对于任何带括号的代码块也是如此。因此,和的值%fullpath%%basename%整个 FOR 循环执行过程中都是恒定的 - 即在进入循环之前存在的值(在本例中未定义)。

解决方法是使用延迟扩展,它发生在命令执行之前。必须先启用延迟扩展才能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!
)

但还有一个潜在的问题。文件名可以包含该!字符,当启用延迟扩展时,任何包含该字符的 FOR 变量!在扩展时都会被破坏。解决方案是在循环内打开和关闭延迟扩展。

@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
)

如果您需要保护!文字,并且需要变量赋值在迭代过程中保持不变,那么最简单的做法是使用 CALLed 过程,以便可以使用正常扩展。只需将 FOR 变量值传输到 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

相关内容