1

Im trying to do a simple batch (it is not the whole thing, but that's the part that is making everything fail)

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

This script is supposed to run from everywherem hence the funny for loop. It is supposed to look through the directory and then do some stuff.

In this particular directory, there's another 3 directories:bomslenovodb, cpat and finance

The expected output

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

The actual output

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

It seems to me that the set fullpath=%%G is not behaving as intended, so the value it's not set properly.

Im on a Windows Server 2008 machine, any ideas?

Hito_kun
  • 113
  • 3

1 Answers1

5

Classic batch mistake :-)

The SET command is working fine. It is your expansion that is failing.

%VAR% expansion occurs when the statement is parsed, and all commands within the FOR loop are parsed at once. The same is true for any parenthesized block of code. So the values of %fullpath% and %basename% are constant throughout the execution of the FOR loop - the values that existed before the loop was entered (undefined in this case).

The fix is to use delayed expansion, which occurs just before the command is executed. Delayed expansion must be enabled with setlocal enableDelayedExpansion before it can be used. And the syntax for expansion changes to !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!
)

But there is still one potential problem. File names can contain the ! character, and any FOR variables containing ! will be corrupted upon expansion when delayed expansion is enabled. The solution is to toggle delayed expansion on and off within the loop.

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

If you need to protect ! literals, and you need your variable assignments to persist accross iterations, then the simplest thing to do is use a CALLed procedure so that you can use normal expansion. Simply transfer the FOR variable value to a CALL parameter. But using CALL is significantly slower than putting everything directly in the loop.

@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
dbenham
  • 651
  • 4
  • 12
  • 1
    You nailed it, man! This is the first time I try to do batch scripting for Windows (Im more of a UNIX guy), so yeah, I'm not surprised by getting stuck in this stuff. Thanks a lot again! – Hito_kun May 07 '14 at 22:26