Search code examples
for-loopbatch-filewhitespace

How to ignore spaces in variable used in a batch FOR loop


I run the google drive account for my children's Scouts BSA troop and as such get hundreds and hundreds of pictures from all of the other parents each event. I created a simple little batch that allows me to put all the pics I get in a directory and rename them "Some_Event-1.png, Some_Event-2.png" etc. and for the most part this works great. But some of the presented filenames have spaces in them and that seems to be causing "file not found" problems when running the batch. How can get the rename command in my FOR loop to function whether or not the filename has a space? Code I'm using below:

(note: I pass flags to the batch rather than use user input in the batch. Format is "BATREN.BAT N EXT New_name" where N/%1 is the number want it to start the numbering at, EXT/%2 is the extension i want to target, eg jpg or png, and New_name/%3 is the new name to assign.)

@ECHO OFF
setlocal enabledelayedexpansion

DIR *.%2 /B>C:\Users\MyUser\AppData\Local\Temp\FileList.txt
Set /A CNT=%1

FOR /F %%i IN (C:\Users\MyUser\AppData\Local\Temp\FileList.txt) DO (
    REN %%i %3-!CNT!.%2
    SET /A CNT+=1
)
EXIT /B

I have tried to find an answer similar to what I'm asking here but there doesn't seem to be one, either I'm blind or an idiot if there already is, so I apologize for that.


Solution

  • Here's a quick example of how I'd approach the task, without the need to create an intermediate text file:

    @Echo Off
    SetLocal EnableExtensions DisableDelayedExpansion
    Set "Count=%~1"
    For /F Delims^=^ EOL^= %%G In ('Dir "*.%~2" /B /A:-D 2^>NUL') Do (
        SetLocal EnableDelayedExpansion
        Ren "%%G" "%~3-!Count!%%~xG"
        EndLocal
        Set /A Count += 1
    )
    

    The example does not validate that any arguments were received, or whether those received are correct. i.e. That the first one is an integer, the second is a string which does not contain a period, and that the third is a string which does not include any characters which are invalid in a Windows file name. I would advise that any robust solution should perform that validation, which is outside of the scope of your question. Additionally there is no check as to whether any of the intended filenames already exist, and whilst it may not be very likely, a robust solution should probably include code for determining what happens in such scenarios.

    The example does however, as per my comment, ensure that there are no delimiters, (delims), and double-quotes the filename strings, to protect spaces and other problematic characters. Additionally it defines no eof, (leading end of line), character, because the default is ; and that is a valid character for a filename to begin with.

    You should also be made aware that on systems which have had 8.3 naming enabled, (default), the file glob example: *.jpe does not list files with the case insensitive extension .jpe, it lists all items whose names end with a period followed by the string jpe followed by zero or more non period characters, which would also include .jpeg. The same is true for *.tif which would also return .tiff files. Now this may not be a problem, and could actually be a benefit, but if it is a possible unwanted issue, you should probably include a filter to ensure that only extensions matching your input are processed, (once again this is outside of the scope of your question).


    If you just want to see what would happen, without actually performing the rename operation, you could change the code to this, which has been modified only so that any output does not omit possible ! characters in the enumerated filenames, (due to delayed expansion):

    @Echo Off
    SetLocal EnableExtensions DisableDelayedExpansion
    Set "Count=%~1"
    For /F Delims^=^ EOL^= %%G In ('Dir "*.%~2" /B /A:-D 2^>NUL') Do (
        Set "FileName=%%G"
        SetLocal EnableDelayedExpansion
        Echo Ren "!FileName!" "%~3-!Count!%%~xG"
        EndLocal
        Set /A Count += 1
    )
    

    You could of course use this for the actual rename task too, by simply removing Echo from line 7.