0

I'm trying to rename all the files in a folder to a number, using this:

setlocal enabledelayedexpansion
set "count=1"
for /f %%f in ('dir /b /o:-d /tc *.jpg') do (
    ren %%f !count!.jpg
    set /a count+=1
)
pause

However, it is only applying this code to the files that do not have () in them. So a file like:

hello.jpg

works, whereas:

hello (0).jpg

does not get picked up. And googling batch script syntax is an exercise in frustration for some reason, so I cannot for the life of me figure out why it's only doing the former.

oguz ismail
  • 34,491
  • 11
  • 33
  • 56
CapnShanty
  • 512
  • 4
  • 15
  • 2
    I don't think the parentheses have anything to do with it. Your second example has a space embedded in it, while the first does not. You need to double-quote filenames with spaces. – Ken White Aug 10 '20 at 02:21
  • if I do that, as in double-quoting "%%f" in both places, it still doesn't treat them as one file, it still throws errors about not being able to find the files and is lopping off everything after the initial name – CapnShanty Aug 10 '20 at 04:01

3 Answers3

2

EDIT :

Just insert text in Double quotes.

Try my way :

@echo off
setlocal enabledelayedexpansion
set "count=1"
Rem Rem "%%~nxf" to get the filename only as described in the reference for for.

for /f  "delims=" %%f in ('dir /b /o:-d /tc *.jpg') do (
    for /F %%q in ("%%~nf") do (
    REN "%%~nxf" "%%q (!count!).jpg"
    )
    set /a count+=1
)
pause
1

I suggest this batch file for renaming all JPEG files with case-insensitive file extension JPG in current folder with a number as file name incremented by one on each JPEG file.

@echo off
setlocal EnableExtensions DisableDelayedExpansion
set "FileNumber=0"
set "TempRename="
for /F "eol=| delims=" %%I in ('dir *.jpg /A-D /B /O-D /TC 2^>nul') do call :RenameFile "%%I"
if defined TempRename for /F "eol=| tokens=2 delims=_" %%I in ('dir !tmp_*.jpg /A-D /B') do ren "!tmp_%%I" "%%I"
goto :EOF

:RenameFile
set /A FileNumber+=1
if %1 == "%FileNumber%%~x1" goto :EOF
if not exist "%FileNumber%%~x1" ren %1 "%FileNumber%%~x1" & goto :EOF
ren %1 "!tmp_%FileNumber%%~x1"
set "TempRename=1"
goto :EOF

The execution environment is set up with the first two command lines which disable command echoing, enable command extensions as required for this batch file for several commands and with disabling delayed environment variable expansion to process correct also file names of JPEG files containing one or more exclamation marks.

Then the environment variable FileNumber is defined with value 0 and the environment variable TempRename is undefined if it is defined by chance on starting this batch file in local environment set up with command SETLOCAL.

Next the command process (cmd.exe) processing the batch file starts in background one more command process because of command FOR with option /F with a command line specified within ' by executing %ComSpec% /c and the command line in ' appended as additional arguments. So there is executed in background with Windows installed into C:\Windows:

C:\Windows\System32\cmd.exe /c dir *.jpg /A-D /B /O-D /TC 2>nul

The internal command DIR of cmd.exe searches in current directory for

  • only files because of option /A-D (attribute not directory)
  • matching the wildcard pattern *.jpg in long or short 8.3 file name
  • and outputs the found file names in bare format because of option /B which means only file name and file extension
  • ordered reverse by creation date which means the file name of the JPEG file created last in current folder is output first and the file name of the JPEG file created first in current folder is output last.

I don't know why the file creation date in current folder is used for this file renaming task and not the last modification date which does not change on copying or moving a file to another folder in comparison to creation date which is set new on a file is copied or moved to another folder. I also don't know why a reverse ordering is done.

2>nul is appended to the DIR command line to suppress the error message output by DIR if it cannot find any entry in current folder which meets the requirements, i.e. there is no *.jpg file in current folder at all, by redirecting the error message from handle STDERR of background command process to device NUL.

Read the Microsoft documentation about Using command redirection operators for an explanation of 2>nul. The redirection operator > must be escaped with caret character ^ on FOR command line to be interpreted as literal character when Windows command interpreter processes this command line before executing command FOR which executes the embedded dir command line in a separate command process started in background.

The output written to handle STDOUT of background command process is captured by FOR and processed line by line after started cmd.exe terminated itself after finishing execution of command DIR.

It is very important to process by FOR a list of file names loaded already completely into memory of command process as the task is to rename files in current folder. It would be not good for various reasons if just a simple FOR loop would be used as in this case the list of file names in current folder changes with each file rename done on iterating over the list of file names. That could cause that some JPEG files are not renamed at all, or are renamed more than once, or FOR becomes an even endless running loop depending on file system. NTFS adds file names different to FAT32 and exFAT to file names table of a folder resulting in different behavior on iterating over the files names of a folder matching a wildcard pattern.

FOR with option /F ignores empty lines which do not occur here and splits up each captured line into substrings using by default normal space and horizontal tab as string delimiters. The line splitting behavior on spaces is not wanted here and for that reason delims= is used to define an empty list of string delimiters to turn off line splitting behavior completely.

FOR with option /F would ignore lines on which first substring (entire file name on using delims=) starts with a semicolon. A file name can start with ; although this is very rare. Therefore eol=| redefines end of line option to a vertical bar which no file name can contain ever and so no file name is ignored by FOR.

The usage of delayed expansion must be avoided to process also successfully file names containing one or more ! which is the reason why the file name assigned currently to the loop variable I is passed enclosed in double quotes to a subroutine name RenameFile. The double quotes are required for file names containing a space or one of these characters &()[]{}^=;!'+,`~.

The subroutine uses first an arithmetic expression to increment the file number by one.

Then it checks if the current file name in double quotes is case-sensitive equal the new file name consisting of the current file number and the file extension of the file in which case the subroutine is left without doing anything at all for this file which has already the correct file name.

Otherwise it is checked if there is no file with new file name in current folder. The file is renamed if this condition is true and the subroutine is left.

The current file is renamed temporarily to !tmp_ and file number appended and keeping file extension if there is already in current folder a file with new file name which will be later renamed. So renaming of current file is more or less queued until all JPEG files in current folder have been processed at least once with renaming all files which could be renamed on first run. This solution works only if there are no file names matching the pattern !tmp_*.jpg in current folder on starting the batch file.

After first FOR processed all captured file names a second FOR is executed only if some file names could not be renamed directly which renames the temporarily renamed files to final file name.

The last command goto :EOF of main code avoids a fall through to code of the subroutine as explained in detail on Where does GOTO :EOF return to? and results in running implicit the command ENDLOCAL to restore initial execution environment explained in detail by this answer.

For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.

  • call /?
  • dir /?
  • echo /?
  • endlocal /?
  • for /?
  • goto /?
  • if /?
  • ren /?
  • set /?
  • setlocal /?

Please note that %1 in subroutine RenameFile references the first argument string exactly as passed to the subroutine which means the file name of current file with the surrounding double quotes.

By the way: Shareware file manager Total Commander has built-in a multi-rename-tool which makes it possible to rename files and folders without having any programming skills and being nevertheless very powerful as even regular expressions can be applied on file names and the resulting file names are displayed before starting the rename operation. The multimedia viewer IrfanView being free for private usage has also a batch rename feature which makes it also possible to rename files without having any coding skills. IrfanView offers also the feature to use data stored inside a JPEG file like date/time on which a photo was made with a camera to put into the file name which would mean using real creation date/time of a photo instead of the date/time on which a file was created in current folder.

Mofi
  • 38,783
  • 14
  • 62
  • 115
0

To directly do what you intend, we do not need to use set /a

@echo off
for /f "tokens=1* delims=[]" %%i in ('dir /b /o:-d /tc *.jpg ^| find /v /n "^"') do (
    ren "%%~j" "%%~nj %%i%%~xj"
)

This does what you want, there is one problem though, if you run this a second time it will rename the same files again, appending yet another number to the name. There are a few ways to sort that out, this one for instance. Assuming your original file does not have numeric value at the end, separated by a space from the filename:

@echo off
for /f "tokens=1* delims=[]" %%i in ('dir /b /o:-d /tc *.jpg ^| find /v /n "^" ^| findstr /VIRC:" [0-9$]"') do (
    ren "%%~j" "%%~nj (%%i)%%~xj"
)
Itchydon
  • 2,293
  • 5
  • 16
  • 30
Gerhard
  • 18,114
  • 5
  • 20
  • 38