Search code examples
windowsbatch-filerenamemove

Renaming multiple files using Batch script


I have a lot of files in this format:

D:\images\AAA_BBB\image\whatever1.jpg  
D:\images\AAA_BBB\image\whatever2.jpg
D:\images\FFF_EEE_CCC\image\asdf1.jpg
D:\images\FFF_EEE_CCC\image\asdf2.jpg
D:\images\WWW_XXX_YYY_ZZZ\image\someimage1.jpg
D:\images\WWW_XXX_YYY_ZZZ\image\someimage2.jpg

I'd like to move and rename these files using a substring of the absolute file path and remove the original files so that the results will look something like the below, starting with 0.

D:\images\AAA\AAA_0.jpg
D:\images\AAA\AAA_1.jpg
D:\images\EEE\EEE_0.jpg
D:\images\EEE\EEE_1.jpg
D:\images\YYY\YYY_0.jpg  
D:\images\YYY\YYY_1.jpg

Note that the number of tokens in the folder with the underscores can be any length.

The following attempt, would only give me the 2nd token starting from the front. Using "Tokens=-2" to get the 2nd last token wouldn't output anything.

@echo off & setlocal EnableDelayedExpansion 

PushD "D:\images" || Exit /B

For /F "EOL=? Delims=" %%G In ('Dir /A:D /B 2^>NUL
 ^| %SystemRoot%\System32\findstr.exe /I /R  "^[^_][^_]*_[^_][^_]*_[^_][^_]*_"'
) Do For /F "Tokens=2 EOL=? Delims=_" %%H In ("%%G") Do (
    Set "i=-1"
    For /F "EOL=? Delims=" %%I In ('Dir "%%G\image" /A:-D /B') Do (
        Set /A i += 1
        SetLocal EnableDelayedExpansion
        Ren "%%G\image\%%I" "%%H_!i!%%~xI" && (
            If Not Exist "%%H\." MD "%%H"
            Move /Y "%%G\image\%%H_!i!%%~xI" "%%H"
        )
        EndLocal
    )
)

PopD

Then I used different logic by trying two for loops to get the last token, and then the 2nd last token, but it didn't work.

@echo off & setlocal EnableDelayedExpansion 

PushD "D:\images" || Exit /B

For /F "EOL=? Delims=" %%G In ('Dir /A:D /B 2^>NUL
 ^| %SystemRoot%\System32\findstr.exe /I /R  "^[^_][^_]*_[^_][^_]*_[^_][^_]*_"'
) Do (
    Set "dirName=%%G"
    Set "lastSegment="
    For %%H In ("!dirName:_=" "!") Do (
        Set "secondLastSegment=!lastSegment!"
        Set "lastSegment=%%~H"
    )
    Set "segment=%%~xG"
    Set "i=-1"
    For /F "EOL=? Delims=" %%I In ('Dir "%%G\image" /A:-D /B') Do (
        Set /A i += 1
        SetLocal EnableDelayedExpansion
        Ren "%%G\image\%%I" "!secondLastSegment!_!i!!segment!" && (
            If Not Exist "!secondLastSegment!\." MD "!secondLastSegment!"
            Move /Y "%%G\image\!secondLastSegment!_!i!!segment!" "!secondLastSegment!"
        )
        EndLocal
    )
)

PopD

Solution

  • This should work:

    @echo off
    setlocal EnableDelayedExpansion
    
    pushd "D:\images" || exit /B
    
    for /D %%d in (*_*) do (
       set "oldName=%%d"
       for %%b in (".!oldName:_=.!") do for %%c in ("%%~Nb") do set "newName=%%~Xc"
       set "newName=!newName:~1!"
       md "!newName!"
       set "i=-1"
       for %%f in ("%%d\image\*.jpg") do (
          set /A i+=1
          move "%%~f" "!newName!\!newName!_!i!%%~Xf"
       )
    )
    

    Your examples have a lot of unneeded or redundant code...

    • In general, it is not necessary to use a for /F command when you just want to process files or folders; plain for or for /D are simpler and works faster than for /F.
    • A for /D %%d in (*_*) do command process all folders with at least one underscore; I don't see why you use a findstr with a complicated regex...
    • See my trick to extract the second last name using ~Name and ~eXtension FOR command parameter modifiers.
    • If you process each source folder once you just need to create the corresponding target folder once. You don't need to check if such a folder exist in each file. If the target folder may previously exist, you can check it just once per source folder...