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