1

As aforementioned in Question title, I have a Batch/CMD(OS: Windows 10 64Bit) script to traverse the subfolders inside %LocalAppData% if the passed argument to script is Chrome, and recursively find and list out all folders containing word Cache at any position.

The script I have come up with relying on for /d /r loop is this:

@echo off && setlocal EnableDelayedExpansion
if /i "%1" equ "chrome" (
    set "subPath0=\Google"
    set "subPath1=\User Data"
    for /d /r "%LocalAppData%!subPath0!\%1" %%i in (*Cache*) do (
        echo "%%i"
    )
) else (
    for /d /r "%Appdata%\%1" %%i in (*Cache*) do (
        echo "%%i"
    )
)

But regardless of I escape the closing brackets inside for loop like (*Cache*^) or not, the above loop is not listing out any *Cache* directories recursively, even though there are actually Cache folders inside the path(the one on which for loop is running) inside User Data folder.

So can anyone point out what am I doing wrong here and help fix this ?

Note: I know Powershell is good option to Batch/CMD, but this is supposedly part of larger script, so appreciate it much if suggestions/fixes in Batch/CMD.

Vicky Dev
  • 1,893
  • 2
  • 27
  • 62

2 Answers2

2

Remove the path from the FOR command and use a PUSHD instead.

@echo on
setlocal EnableDelayedExpansion
if /i "%1" equ "chrome" (
    set "subPath0=\Google"
    set "subPath1=\User Data"
    pushd "%LocalAppData%!subPath0!\%~1"
    for /D /R %%i in (*Cache*) do (
        echo "%%i"
    )
    popd
) else (
    pushd "%Appdata%\%1"
    for /d /r %%i in (*Cache*) do (
        echo "%%i"
    )
    popd
)

Why not simplify it to one FOR command?

@echo on
setlocal EnableDelayedExpansion
if /i "%~1" equ "chrome" (
    set "subPath0=\Google"
    set "subPath1=\User Data"
    set "searchpath=%LocalAppData%!subPath0!\%1"

) else (
    set "searchpath=%Appdata%\%~1"
)

for /d /r "%searchpath%" %%i in (*Cache*) do echo "%%i"
Squashman
  • 13,649
  • 5
  • 27
  • 36
  • That works smooth, thank you, but is there any specific reason/limitation that passing a constructed path, inside which there's a single subdirectory and then inside that there's ton of folders named `Cache`, directly to `for /d /r` loop doesn't work ? Because it worked fine for `Cache` folders inside `%appdata%\%1`... Also if possible, please add some explanation to your answer. – Vicky Dev Feb 04 '22 at 17:29
  • I have no explanation as to why it has to be written like that. I just know I have run into that same issue in the past and knowing that you can also use the `CD` or `PUSHD` command to set the working directory is a very valid work around. As far as I know the usage of using `/D` and `/R` together is an undocumented feature. So you may get results you are not expecting. – Squashman Feb 04 '22 at 17:33
  • Yes that's quite an improvement, accepted, thanks again !! – Vicky Dev Feb 04 '22 at 17:50
  • 1
    @VickyDev this was a known issues that we discussed on [dostips.com](https://www.dostips.com/forum/viewtopic.php?p=34488) many years ago. – Squashman Feb 04 '22 at 17:54
  • 2
    So the second `FOR` command worked in your code because those variables are already defined before the code block is executed. Delayed Expansion variables will not work for `For` options. – Squashman Feb 04 '22 at 17:56
  • My question [Why is escaping exclamation marks not necessary in parameter of `for /F` or `for /R`?](https://stackoverflow.com/q/39649641) is somehow related, and its answer explains why delayed expansion cannot be used in the path parameter of the `/R` option, neither can be used `for` meta-variables… – aschipfl Feb 04 '22 at 18:50
2

I strongly recommend to use:

@echo off & setlocal EnableExtensions DisableDelayedExpansion
if /I not "%~1" == "chrome" goto SearchAppdata

for /D /R "%LocalAppData%\Google\%~1" %%i in (*Cache*) do echo "%%i"
goto EndBatch

:SearchAppdata
for /D /R "%Appdata%\%~1" %%i in (*Cache*) do echo "%%i"

:EndBatch
endlocal

The explanations for all the changes:

  1. The command echo off never fails and so the unconditional operator & is used instead of the conditional operator && in the first line. For details see single line with multiple commands using Windows batch file.

  2. The batch file requires enabled command extensions. For that reason it is advisable to explicitly enable the command extensions and do not depend on being enabled outside of the batch file.

  3. Delayed expansion is explicitly not used because of that would result in %~1, %%i, %LocalAppdata% or %Appdata% expanding to a string with one or more ! would not be processed correct anymore with interpreting the exclamation mark(s) as literal characters.

  4. The string comparison operator == is used to compare the two strings instead of the integer comparison operator equ working also with strings, but different to the string comparison operator in some rare uses cases. See symbol equivalent to NEQ, LSS, GTR, etc. in Windows batch files for more details.

  5. The first argument string passed to the batch file is processed with removal of " at beginning and at end if being enclosed on both sides in " as otherwise the batch file processing stops already on second line due to a serious syntax error. That can still occur if somebody runs the batch files with something like google", i.e. there is a double quote at the end of the argument string, but no one at the beginning. It is possible to handle this extremely rare use case also with additional command lines, but that should not be really necessary here because of that is a syntax error by the program or user starting the batch file.

  6. The command GOTO is used to avoid command blocks which makes it possible to reference all environment variables without delayed expansion.

The command FOR does not support delayed expansion inside the path specified after /R. That is a known issue of this command documented by the DosTips forum topic Limitation in for /r command. The solution is avoiding delayed expansion inside the directory path string specified after /R.

There can be used before CD (not working for UNC paths) or PUSHD (works also for UNC paths with command extensions enabled) and POPD after the loop. Or there is used the command DIR executed by one more command process started in background which has the advantage to find also hidden directories containing the string cache in name which for /D ignores while for /R traverses also through hidden directories.

@echo off & setlocal EnableExtensions DisableDelayedExpansion
if /I not "%~1" == "chrome" goto SearchAppdata

for /F "delims=" %%i in ('dir "%LocalAppData%\Google\%~1\*Cache*" /AD-L /B /S 2^>nul') do echo "%%i"
goto EndBatch

:SearchAppdata
for /F "delims=" %%i in ('dir "%Appdata%\%~1\*Cache*" /AD-L /B /S 2^>nul') do echo "%%i"

:EndBatch
endlocal

Reparse points (directory junctions, symbolic links) are explicitly ignored in this case because of option /AD-L (attribute directory, but not attribute link), see SS64 - MKLINK for more information about links on Windows.

Mofi
  • 46,139
  • 17
  • 80
  • 143