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

Hit*_*kun 1 windows-server-2008 batch-file windows-command-prompt

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

@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%
)
Run Code Online (Sandbox Code Playgroud)

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

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

预期输出

e:\tmp\bomslenovodb
e:\tmp\bomslenovodb
bomslenovodb
e:\tmp\cpat
e:\tmp\cpat
cpat
e:\tmp\finance
e:\tmp\finance
finance
Run Code Online (Sandbox Code Playgroud)

实际输出

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
Run Code Online (Sandbox Code Playgroud)

在我看来,它的set fullpath=%%G行为不符合预期,因此它的值设置不正确。

我在 Windows Server 2008 机器上,有什么想法吗?

dbe*_*ham 5

经典的批处理错误:-)

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

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

修复方法是使用延迟扩展,它发生在命令执行之前。延迟扩展必须先启用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!
)
Run Code Online (Sandbox Code Playgroud)

但是仍然存在一个潜在的问题。文件名可以包含!字符,并且!当启用延迟扩展时,任何包含的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
)
Run Code Online (Sandbox Code Playgroud)

如果您需要保护!文字,并且需要您的变量赋值在迭代中持续存在,那么最简单的方法是使用 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
Run Code Online (Sandbox Code Playgroud)