Search code examples
windowsfor-loopbatch-filecmd

Batch Recursively Launch Specific .exe


In windows I have a batch file, named the same as a folder in its current directory.

I am trying to recursively launch a specific .exe file that is a few folders deep in the above mentioned folder.

I am trying:

set "NAME=DRAGON QUEST XI S.exe"
for /R "%~n0\" %%I in ("%NAME%") do if exist "%%I" start "" /WAIT "%%I"

But that is trying to launch DRAGON.exe, then QUEST.exe, then XI.exe then S.exe and I don't understand why.

I think it will have something to do with quotes, but have tried adding/removing them from everywhere I could see and no luck.

Adding a wildcard to ("%NAME%*") works, but could be troublesome if there is more than one .exe that starts with DRAGON.


Solution

  • What you have encountered is caused by something that I consider a terrible design flaw of the for /R loop and expansion of its meta-variable together with the ~-modifiers.

    Create the following batch file and execute it:

    @for /R "%~dp0." %%I in (foo bar "foo bar" "foo bar?") do @(
        echo/
        echo Modifier '~nx': %%~nxI
        echo No modifiers  : %%I
        echo Modifier '~'  : %%~I
        echo Modifier '~f' : %%~fI
        echo '~' and quoted: "%%~I"
    )
    

    The output will be this, given that there are two files foo bar and foo barS available in the directory of the batch file:

    Modifier '~nx': foo
    No modifiers  : D:\test dir\foo
    Modifier '~'  : D:\test dir\foo
    Modifier '~f' : D:\test dir\foo
    '~' and quoted: "D:\test dir\foo"
    
    Modifier '~nx': bar
    No modifiers  : D:\test dir\bar
    Modifier '~'  : D:\test dir\bar
    Modifier '~f' : D:\test dir\bar
    '~' and quoted: "D:\test dir\bar"
    
    Modifier '~nx': "foo bar"
    No modifiers  : D:\test dir\"foo bar"
    Modifier '~'  : D:\test dir\"foo bar"
    Modifier '~f' : D:\test dir\"foo bar"
    '~' and quoted: "D:\test dir\"foo bar""
    
    Modifier '~nx': foo bar
    No modifiers  : D:\test dir\foo bar
    Modifier '~'  : D:\test dir\foo bar
    Modifier '~f' : D:\test dir\foo bar
    '~' and quoted: "D:\test dir\foo bar"
    
    Modifier '~nx': foo barS
    No modifiers  : D:\test dir\foo barS
    Modifier '~'  : D:\test dir\foo barS
    Modifier '~f' : D:\test dir\foo barS
    '~' and quoted: "D:\test dir\foo barS"
    

    It seems that the ~-modifiers (which remove potential surrounding quotation marks) are applied too late, namely after preceding an iterated item with the absolute root path. A standard for loop does not precede anything, so the problem cannot arise and the ~-modifiers may see a quoted string (remove /R "%~dp0." from the batch file, run it again from its parent directory and check quotation in the output).

    The line that reflects our situation is the one '~' and quoted: "D:\test dir\"foo bar""; due to its wrong quotation, the command parser unintentionally recognises two different tokens "D:\test dir\"foo and bar"" in the expanded value of "%%~I".


    To return to your code, there are the following options to work around the bad behaviour of for /R:

    1. Append a wildcard (best to use ? since it matches just a single character and therefore reduces the chance of matching unwanted items) and then recheck the name of the iterated item:

      set "NAME=DRAGON QUEST XI S.exe"
      for /R "%~n0" %%I in ("%NAME%?") do (
          if /I "%%~nxI"=="%NAME%" (
              if exist "%%I" (
                  ECHO start "" /WAIT "%%I"
              )
          )
      )
      
    2. Let dir /S return the matching file(s) and use for /F to process its output:

      set "NAME=DRAGON QUEST XI S.exe"
      for /F "delims=" %%I in ('dir /S /B /A:-D-H-S "%~n0\%NAME%"') do (
          if exist "%%I" (
              ECHO start "" /WAIT "%%I"
          )
      )