Search code examples
windowsbatch-filerenamefilenames

Is there any way to remove specific characters from a filename in Windows, using a batch file?


I would like to rename every file in a directory that contains '[' or ']' characters by removing any instance of those two characters from those filenames.

I have yet to determine any way to read filenames character-by-character with a batch file.

Thanks!


Solution

  • You don't need to read the filename character by character. In batch you can simply replace all occurrences of a substring subs with another substring repl in the value of a variable %var% with %var:subs=repl%. As removing is the same as replacing with an empty string (in batch at least), %var:h=% will remove all hs from the value of the var variable. So all you need is to store the filename in a variable and you can have the new filename without [ and/or ].
    To iterate over all files in a directory you'll just need a FOR loop.

    So you can iterate over all files in the current directory and rename the ones with [ or ] in their name (or both) with the following script (see edit for version which also handles names with exclamation marks):

    @echo off
    Setlocal EnableDelayedExpansion
    
    FOR %%G IN (*) DO (
        set "filename=%%~G"
        set "filename=!filename:[=!"
        set "filename=!filename:]=!"
        REM Rename only if there is a difference between new filename and old filename in %%G
        IF NOT "!filename!" == "%%~G" ren "%%~G" "!filename!"
    )
    EndLocal
    exit /b 0
    

    Because the filename variable is set inside the FOR block and we want to read it inside the same block, we need delayed expansion.

    EDIT: As @dbenham said in comment, delayed expansion can cause problems when you have filenames containing exclamation marks ! (%%~G will then contain a ! which normally announces a variable being expanded with delayed expansion). To solve that problem, @dbenham proposed a nice solution: enable delayed expansion only when we're about to use it i.e. inside the FOR block, just before using the filename variable. The ! won't poisin the value of a variable expanded with delayed expansion so !filename! can be used without a ! being misinterpreted in its value. This also means we'll have to move EndLocal to after we're done using delayed expansion in the FOR block (i.e. at the end of the block) and avoid the use of %%~G in the ren command. The latter can be done by using a variable to hold the original name (also before enabling delayed expansion) and use delayed expansion to retrieve the original name. That variable can then actually be used to construct the new filename.

    @echo off
    
    FOR %%G IN (*) DO (
        REM Set filename without delayed expansion, no misinterpreted '!' possible
        set "old_filename=%%~G"
        REM before using old_filename, enable delayed expansion
        SetLocal EnableDelayedExpansion
        set "new_filename=!old_filename:[=!"
        set "new_filename=!new_filename:]=!"
        REM Rename only if there is a difference between new filename and old filename in %%G
        ren "!old_filename!" "!new_filename!"
        REM No more delayed expansion needed in block now
        EndLocal
    )
    
    exit /b 0
    

    Also, @KlitosKyriacou pointed out the IF was actually unnecessary: renaming a file with its original filename causes no harm. I've left it out in my 2nd version (the edit-version).