<http://www.netikka.net/tsneti/info/tscmd107.htm>
Copyright © 2003-2012 by Prof. Timo Salmi  
Last modified Mon 23-Apr-2012 19:46:44

 
Assorted NT/2000/XP/.. CMD.EXE Script Tricks
From the html version of the tscmd.zip 1cmdfaq.txt file
To the Description and the Index
 

This page is edited from the 1cmdfaq.txt faq-file contained in my tscmd.zip command line interface (CLI) collection. That zipped file has much additional material, including a number of detached .cmd script files. It is recommended that you also get the zipped version as a companion.

Please see "The Description and the Index page" for the conditions of usage and other such information.



107} How to do non-trivial renaming such as IMG*.jpg to IMG_*.jpg?

Consider the following situation
  Rename IMG1225.jpg to IMG_1225.jpg
  Rename IMG1226.jpg to IMG_1226.jpg
  Rename IMG1227.jpg to IMG_1227.jpg
  Rename IMG1228.jpg to IMG_1228.jpg

First off this can't be solved as
  ren C:\WHATEVER\IMG*.jpg IMG_*.*
since it would give you (dropping the thousands):
  IMG_225.jpg
  IMG_226.jpg
  IMG_227.jpg
  IMG_228.jpg

If you try
  @echo off & setlocal enableextensions enabledelayedexpansion
  for %%f in (C:\WHATEVER\*.jpg) do (
    rem echo %%~df%%~pf%%~nf%%~xf
    set base=%%~nf
    set base=!base:IMG=IMG_!
    ren %%~df%%~pf%%~nf%%~xf !base!%%~xf
    )
  endlocal & goto :EOF
you'll get
  IMG_1226.jpg
  IMG_1227.jpg
  IMG_1228.jpg
  IMG__1225.jpg

The cmd.exe is a command line interpreter, not a compiler. The renaming forms a kind of a ring with the first of your files popping up anew renamed within the execution. The solution is to use a formulation of FOR which does not have that "bug" or "feature" in cmd.exe.
  @echo off & setlocal enableextensions enabledelayedexpansion
  for /f "delims=" %%f in (
    'dir /a:-d /b /s /o:n C:\WHATEVER\*.jpg') do (
    set base=%%~nf
    set base=!base:IMG=IMG_!
    ren "%%~df%%~pf%%~nf%%~xf" "!base!%%~xf"
    )
  endlocal & goto :EOF
This time you will get
  IMG_1225.jpg
  IMG_1226.jpg
  IMG_1227.jpg
  IMG_1228.jpg

Note that the above solution will recurse to subfolders, as well. If you do not want that, use
  @echo off & setlocal enableextensions enabledelayedexpansion
  set folder_=C:\WHATEVER
  ::
  for /f "delims=" %%f in (
    'dir /a:-d /b /o:n %folder_%\*.jpg') do (
    set base=%%~nf
    set base=!base:IMG=IMG_!
    ren "%folder_%\%%~nf%%~xf" "!base!%%~xf"
    )
  endlocal & goto :EOF
The dilemma with dir /b without the /s is that %%~df and %%~pf will not point where one would intuitively expect them to point. Instead they point to the script's default folder. Thus the above alternative is needed.

Incidentally, the IMG_1225.JPG format is the naming convention for digiphotos on a Canon Ixus. It might be used on other cameras as well, but that I know of.

Consider another, similar renaming task, which often appears as a FAQ in slight variations. The following example recounts a true situation which I had myself. A set of digiphotos I had needed to be renamed as follows:
In a folder C:\_L\PHOTO
  W2010042901.JPG --> W2010042906.JPG
  W2010042902.JPG --> W2010042907.JPG
  W2010042903.JPG --> W2010042908.JPG
  :
  W2010042922.JPG --> W2010042927.JPG

  @echo off & setlocal enableextensions enabledelayedexpansion
  for /L %%n in (22,-1,1) do (
    set n1_=00000%%n
    set n1_=!n1_:~-2!
    set /a n2_=%%n+5
    set n2_=00000!n2_!
    set n2_=!n2_:~-2!
    echo ren C:\_L\PHOTO\W20100429!n1_!.JPG W20100429!n2_!.JPG
    )
  endlocal & goto :EOF

The (redirectable) output will be
  C:\>C:\_D\TEST\CMDFAQ.CMD
  ren C:\_L\PHOTO\W2010042922.JPG W2010042927.JPG
  ren C:\_L\PHOTO\W2010042921.JPG W2010042926.JPG
  ren C:\_L\PHOTO\W2010042920.JPG W2010042925.JPG
  :
  ren C:\_L\PHOTO\W2010042902.JPG W2010042907.JPG
  ren C:\_L\PHOTO\W2010042901.JPG W2010042906.JPG
Observe that:
  1) The renaming is done in a reverse order.
  2) Numbers below ten need be padded with a leading zero.

Take the following, true-life renaming task
In a folder C:\_M\Orig rename as follows
  W2012022901.JPG --> Worig2012022901.JPG
  W2012022902.JPG --> Worig2012022902.JPG
  :
  W2012030907.JPG --> Worig2012030907.JPG
  W2012030908.JPG --> Worig2012030908.JPG

  @echo off & setlocal enableextensions enabledelayedexpansion
  set folder_=C:\_M\Orig
  for /f "usebackq tokens=* delims=" %%f in (
    `dir /b /o:d "%folder_%\*.JPG"`) do (
      call :GetFirstChar "%%f" firstchar_
      call :GetFromSecondChar "%%f" fromsecond_
      echo ren "%folder_%\%%f" "!firstchar_!orig!fromsecond_!")
  endlocal & goto :EOF
  ::
  :GetFirstChar
  setlocal
  set return_=%~1
  set return_=%return_:~0,1%
  endlocal & set "%2=%return_%" & goto :EOF
  ::
  :GetFromSecondChar
  setlocal
  set return_=%~1
  set return_=%return_:~1%
  endlocal & set "%2=%return_%" & goto :EOF

The (redirectable) output will be
  C:\>C:\_D\TEST\CMDFAQ.CMD
  ren "C:\_M\Orig\W2012022901.JPG" "Worig2012022901.JPG"
  ren "C:\_M\Orig\W2012022902.JPG" "Worig2012022902.JPG"
  :
  ren "C:\_M\Orig\W2012030907.JPG" "Worig2012030907.JPG"
  ren "C:\_M\Orig\W2012030908.JPG" "Worig2012030908.JPG"
In GetFromSecondChar in %return_:~1% note that in command-line programming the index of the second character is one, not two, since the index of the first character in cmd-line programming is zero.

How about reversing the results?
  @echo off & setlocal enableextensions enabledelayedexpansion
  set folder_=C:\_M\Orig
  for /f "usebackq tokens=* delims=" %%f in (
    `dir /b /o:d "%folder_%\*.JPG"`) do (
      call :GetFirstChar "%%f" firstchar_
      call :GetFromSixthChar "%%f" fromsixth_
      echo ren "%folder_%\%%f" "!firstchar_!!fromsixth_!")
  endlocal & goto :EOF
  ::
  :GetFirstChar
  setlocal
  set return_=%~1
  set return_=%return_:~0,1%
  endlocal & set "%2=%return_%" & goto :EOF
  ::
  :GetFromSixthChar
  setlocal
  set return_=%~1
  set return_=%return_:~5%
  endlocal & set "%2=%return_%" & goto :EOF

You might wish to see also Item #12 and Item #178.