7

I am running a Batch script for processing some files. When it determines that the current file needs no processing I would like to skip the rest of the commands for that file and move directly to the next one. How? Goto :EOF and exit /b both exit the entire script.

Here is the script. It takes a directory and renames all the files to include all the words from parent directory names except the ones already included in the filename.

@echo off
setlocal enabledelayedexpansion

rem set input directory (add check for validity)
set "inputDir=%~1"
set "confirmation=false"

:proceed
for /r "%inputDir%" %%F in (*.*) do (
    rem use path to file as keywords
    set "words=%%~pF"
    rem replace known useless marks with spaces
    set "words=!words:\= !"
    set "words=!words:_= !"
    set "words=!words:,= !"
    set "words=!words:  = !"
    rem remove known useless words
    set "words=!words: The = !"
    set "words=!words: A = !"
    set "words=!words: An = !"
    set "words=!words: Video = !"
    set "words=!words: New Folder = !"
    rem remove word that already exists in the filename
    for %%W in (!words!) do echo %%~nF | find /i "%%W" >nul && set "words=!words: %%W = !"
    rem replace leading and trailing spaces with brackets
    set "words=[!words:~1,-1!]"
    rem if all words already included end task for current file
    if "!words!"=="[]" exit /b
    rem build the new filename
    set "newName=%%~nF !words!%%~xF"
    rem fix "] [" caused by repeated renaming, causes fusion with non-related bracketed sets, which is fine
    set "newName=!newName:] [= !"
    rem task for displaying the name change for confirmation
    if /i not "%confirmation%"=="yes" echo old "%%F" && echo new "!newName!"
    rem task for doing the actual rename if confirmed
     if /i "%confirmation%"=="yes" echo ren "%%~nxF" "!newName!" & echo ren "%%F" "!newName!"
)

rem fails to rename files with ! mark in the filename. no other trouble, just won't rename those. error: the syntax of the command is incorrect

rem if coming from second (completed) run then exit
if /i "%confirmation%"=="yes" exit /b
rem ask for confirmation to run again for real
set /p confirmation="confirm you want to perform all these rename tasks (type yes or no)?: "
if /i not "%confirmation%"=="yes" echo confirmation denied
if /i "%confirmation%"=="yes" goto proceed

endlocal

I am also trying an alternate method of conditionally running the last commands like so

@echo off
setlocal enabledelayedexpansion

rem set input directory (add check for validity)
set "inputDir=%~1"
set "confirmation=false"

:proceed
for /r "%inputDir%" %%F in (*.*) do (
    rem use path to file as keywords
    set "words=%%~pF"
    rem replace known useless marks with spaces
    set "words=!words:\= !"
    set "words=!words:_= !"
    set "words=!words:,= !"
    set "words=!words:  = !"
    rem remove known useless words
    set "words=!words: The = !"
    set "words=!words: A = !"
    set "words=!words: An = !"
    set "words=!words: Video = !"
    set "words=!words: New Folder = !"
    rem remove word that already exists in the filename
    for %%W in (!words!) do echo %%~nF | find /i "%%W" >nul && set "words=!words: %%W = !"
    rem replace leading and trailing spaces with brackets
    set "words=[!words:~1,-1!]"
    rem if all words not already included in the filename do
    if not "!words!"=="[]" (
        rem build the new filename
        set "newName=%%~nF !words!%%~xF"
        rem fix "] [" caused by repeated renaming, causes fusion with non-related bracketed sets, which is fine
        set "newName=!newName:] [= !"
        rem task for displaying the name change for confirmation
        if /i not "%confirmation%"=="yes" echo old "%%F" && echo new "!newName!"
        rem task for doing the actual rename if confirmed
        if /i "%confirmation%"=="yes" echo ren "%%~nxF" "!newName!" & echo ren "%%F" "!newName!"
    )
)

rem fails to rename files with ! mark in the filename. no other trouble, just won't rename those. error: the syntax of the command is incorrect

rem if coming from second (completed) run then exit
if /i "%confirmation%"=="yes" exit /b
rem ask for confirmation to run again for real
set /p confirmation="confirm you want to perform all these rename tasks (type yes or no)?: "
if /i not "%confirmation%"=="yes" echo confirmation denied
if /i "%confirmation%"=="yes" goto proceed

endlocal

But this is a disaster, resulting in filenames that are cut from seemingly random parts of the filename with words added.

4
  • 1
    if( file is OK) continue : This will go to end of the loop and process the next item. Also check out the break command, this exits the loop.
    – cliff2310
    Feb 25 at 19:42
  • 3
    This is painful to watch, especially if it is self-inflicted. An alternative is PowerShell. It runs on Windows XP (at least the 64-bit version) and later. Feb 25 at 21:47
  • 2
    @PeterMortensen As someone whose first experience of systems scripting was Batch and who definitely does not miss it, I'd still take it over PowerShell anyday. Powershell is comically verbose and for me uglier than Batch, I've only ever pasted snippets of it and I can't imagine having to type it. Bricktop: you take sugar? ;) If you can get WSL2 working use that - if not or if you need better speed and access to the raw block-devices for tools like dd, use Cygwin. Feb 27 at 1:36
  • Even if it's uglier, although being more verbose is a weird reason to dislike it IMO, it is much more understandable and quicker to use for common tasks. E.g. this could be rewritten in PowerShell with at least 3x less lines and no 2000 issues like "how to skip a loop cycle".
    – Destroy666
    Mar 6 at 12:24

3 Answers 3

18

Just place a label :nextiteration (or whatever other name you like) as the last statement inside the for-loops body and goto to that label when you decide that you can skip the rest of the for-body.

Because a closing parenthesis on a FOR body has to be preceded by a regular statement (a label won't do) you will have to place a dummy statement (like a REM) between the label and the closing parenthesis.

FOR xxxxxxxxx DO (
  some statements (executed on each iteration)
  IF {some condition is met} GOTO nextiteration
  statements that will be skipped if conditon is met
:nextiteration
  REM to prevent syntax error
)
0
4

Windows batch doesn't have too many options for skipping an iteration of the loop. The methods you may use are:

  • Put an IF block around the part of the loop that you would like to skip:

    @Echo Off
    if A equ A (
      IF B==C (
        Echo shouldn't go here
      )
    )
    
  • Use call to call a routine that contains the entire inner loop, where exit/B will work:

     @Echo Off
     Setlocal 
     For %%A in (alpha beta gamma) DO (
       Echo Outer loop %%A
       Call :inner 
     )
     Goto :eof
    
     :inner
     For %%B in (U V W X Y Z) DO (
       if %%B==X ( exit /b 2 )
       Echo    Inner loop    Outer=%%A Inner=%%B
     )
    
  • Use the command goto :Label and put Label: at the end of the loop. This will probably not work in your case because of a known bug: Using GOTO within parentheses, including FOR and IF commands, will break it.
    An example that shows the bug :

    @Echo Off
    if A equ A (
      IF B==C GOTO specialcase 
        Echo shouldn't go here
        goto:eof 
    :specialcase
    )
    

Reference :

5
  • The third option seems to terminate the entire loop, not skip a single cycle
    – Bricktop
    Feb 25 at 15:07
  • I am not sure what you mean by the first method. The second method doesn't seem to work, producing error ) was unexpected at this time. Do you mean to place it after the loop? That would also terminate the entire loop, not just skip a single cycle.
    – Bricktop
    Feb 25 at 15:17
  • Add to your post (short) examples for the code that doesn't work and I'll have a look.
    – harrymc
    Feb 25 at 15:23
  • Examples of what? exit /b is the problem in my first version. And the second the problem is produced somehow because of the conditional second brackets set.
    – Bricktop
    Feb 25 at 15:51
  • 1
    I added examples. You're right about the last point : There's a bug and GOTO within parenthesis doesn't work.
    – harrymc
    Feb 25 at 18:27
0

Try using For /F... and ... | findstr /c:"string1" /c:"string2"...

Use a loop that allows you to filter the strings and thus give more control over the actions with the resulting interrese file names, this makes it easier to give you interactions to rename them without trying to skip through the loop as it becomes more precise:

@echo off & setlocal

2>nul cd /d "%~1" || exit 

setlocal enabledelayedexpansion

for %%G in =;( 
     "_",",","  "," The "," A "," An "," Video "," New Folder " 
    );= do if not defined _str2find =;( set "_str2find=/c:"%%~G""
        );= else call set "_str2find=!_str2find! /c:"%%~G""

setlocal disabledelayedexpansion

for /f usebackq^delims^= %%G in (`
    2^>nul where /r . *.* ^| findstr %_str2find% `);= do =;( 
      echo/%%~G 
      :: So you don't need to use skip, the rest of your 
      :: interactions continue here and within your loop 
     );=

endlocal 

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .