Search code examples
windowsbatch-filefile-renamebatch-rename

Batch rename extension to lowercase


I can rename a folder of .TXT files to a lowercase extension with

ren *.TXT *.txt

But that's because I know the extension, of which there is only one. Is there a one liner for looping over a directory and changing all the extensions to lowercase?

I tried:

for %%f in (*.*) do (rename "%%f" "%%~xf")

That works...to some extent, but it also removes the filename part. D'oh!

Fail better.


Solution

  • @ECHO OFF
    SETLOCAL
    SET "sourcedir=U:\sourcedir"
    
    FOR /f "delims=" %%A IN (
     'dir /b /a-d "%sourcedir%\*" '
     ) DO (
     FOR /f "delims=" %%a IN (
      'dir /b /l "%sourcedir%\%%A" '
      ) DO (
      if "%%~xa" neq "%%~xA" ECHO REN "%sourcedir%\%%A" "%%~nA%%~xa"
    )
    )
    
    GOTO :EOF
    

    Naturally, the two nested for commands may be compacted to a single line if desired - shown here on multiple lines for legibility.

    You would need to change the setting of sourcedir to suit your circumstances.

    The required REN commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO REN to REN to actually rename the files.

    Note that %%A retains the original character-case but %%a (a completely different variable) acquires the lower-case equivalent because of the /l switch on the dir command.


    Following Aacini's response, more extensive testing (well, actually trying to rename files rather than just generate a rename instruction that should on first glance work) revealed this doesn't actually work, possibly because the OS sees a file being renamed to its own name (disregarding case). Hmph.

    So - first step was to rename twice - first to a dummy name, and then from the dummy name to the required name.

    Fine in theory. Problem was that the ~x operator doesn't seem to consistently report the same string - almost depending on how many times it's used. So - I tried executing a subroutine, passing the "to" and "from" filenames. The subroutine seemed to have the same problems when attempting to access the name fragments of the parameters. Gloom.

    So I assigned the name to a variable and tried processing that. Repeatedly remove the string before . in the name until there are no more dots and you have the extension.

    Then remove the extension from the filename, but can't use the replace-with-nothing method since the filename may be whatever.txt.txt for instance. So lop off the last few characters one at a time within a loop for the length of the extension found+1 (for the dot).

    So - if the lower-case version of the extension found is the same as the current-case version in ext2, no rename required. Otherwise, rename to the original-case name+the lower-case extension. Via a dummy name to ensure the OS doesn't get too smart for itself.

    And here's the result. Subject to unexpected results with the usual characters, of course.

    @ECHO OFF
    SETLOCAL
    SET "sourcedir=U:\sourcedir"
    :choosedummy
    SET "dummyname=dummyname.%random%"
    IF EXIST "%sourcedir%\%dummyname%" GOTO choosedummy
    
    FOR /f "delims=" %%A IN (
     'dir /b /a-d "%sourcedir%\*" '
     ) DO (
     FOR /f "delims=" %%a IN (
      'dir /b /l "%sourcedir%\%%A" '
      ) DO CALL :casechg "%%a" "%%A"
    )
    
    GOTO :EOF
    
    :casechg
    SET "ext2=%~2"
    SET "name2=%~2"
    :ext2loop
    IF "%ext2%" neq "%ext2:*.=%" SET "ext2=%ext2:*.=%"&GOTO ext2loop
    
    :: if extension cases are identical, or no extension, skip rename
    IF "%~x1" equ "" GOTO :EOF 
    IF "%~x1" equ ".%ext2%" GOTO :EOF 
    
    SET "lop=%ext2%"
    :loploop
    SET "name2=%name2:~0,-1%"
    IF DEFINED lop SET "lop=%lop:~1%"&GOTO loploop
    
    REN "%sourcedir%\%~2" "%dummyname%"
    REN "%sourcedir%\%dummyname%" "%name2%%~x1"
    GOTO :eof