In my work we frequently run into a situation where we have folders of hundreds of outputted PDFs. These all have the same naming scheme which is:
[Number/Text String]-XYZ [More Text]
They all have a 3 letter code after the initial document title - it's not always XYZ it can be any 3 capitalized letters.
In order to submit these the file names need to be reduced down, everything after the 3 letter code needs to be removed and everything before needs to be retained. The string before the code and the text after the code vary in both the content and the number of characters so the code doesn't have a consistent position within the name.
At the moment the solution is to manually go through and delete the text from the end of the file name, this takes ages and I thought there must be a better solution.
What I would ideally like is a .Bat file that I (or my colleagues) can drop into any given folder and run it to mass rename the files. I have done some testing of my own but I cannot quite get it to work as I would like.
This is what I've got at the moment:
@echo off
setlocal enabledelayedexpansion
echo Starting the renaming process...
REM Loop through all PDF files in the current directory
for %%f in (*.pdf) do (
set "filename=%%~nf"
set "extension=%%~xf"
REM Debug: Show the current filename being processed
echo Processing: %%f
REM Check if "XYZ" is in the filename
echo !filename! | findstr /C:"XYZ" >nul
if !errorlevel! equ 0 (
echo Found "XYZ" in: !filename!
REM Retain everything up to and including "XYZ"
for /f "tokens=1 delims=XYZ" %%a in ("!filename!") do (
set "newname=%%a"
)
REM Append "XYZ" to the new name
set "newname=!newname!XYZ"
REM Debug: Show the new name before renaming
echo New name will be: "!newname!!extension!"
REM Check for duplicates and rename
if not exist "!newname!!extension!" (
ren "%%f" "!newname!!extension!"
echo Renamed "%%f" to "!newname!!extension!"
) else (
echo Skipping "%%f" because a file named "!newname!!extension!" already exists.
)
) else (
echo "XYZ" not found in: !filename!
)
)
REM Pause to see the output
echo Finished processing. Press any key to exit.
pause
endlocal
I am aware this is terrible and am expecting to be criticized for my poor scripting, but this actually works decently. It locates the code and removes the stuff after it.
The issue I'm having is if the string before the code contains any of the letters in the code then they are removed also. It also feels clunky and as someone not well versed in this I thought it would be prudent to seek advice.
Would appreciate any help or tips you could give me.
P.S. I am aware that Windows Powershell would be a far better tool for this. However it's blocked on our system for some reason.
@ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
rem The following setting for the directory is a name
rem that I use for testing and deliberately includes spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files"
SET "targetstring=XYZ"
SET "requiredextension=pdf"
FOR %%e IN ("%sourcedir%\*%targetstring%*.%requiredextension%") DO (
rem %%e has the full name of the file detected
ECHO processing %%e
rem ensure targetstring found is upper-case
SET "originalname=%%~ne"
ECHO %%~ne |FINDSTR /L /c:"%targetstring%" >NUL
IF ERRORLEVEL 1 (
ECHO skipping %%~nxe - does NOT contain upper-case "%targetstring%"
) ELSE (
CALL :subsuconly
IF EXIST "%sourcedir%\!newname!%%~xe" (
ECHO Cannot RENAME "%%~nxe" to "!newname!%%~xe" as "!newname!%%~xe" already exists
) ELSE (
ECHO Renaming "%%~nxe" to "!newname!%%~xe"
ECHO REN "%%e" "!newname!%%~xe"
)
)
)
GOTO :EOF
::
:: Substitute only the upper-case targetstring
:subsuconly
SET "newname="
:: first 3 characters of copyname are targetstring?
:subsloop
IF "%originalname:~0,3%"=="%targetstring%" SET "newname=%newname%%targetstring%"&GOTO :eof
SET "newname=%newname%%originalname:~0,1%"
SET "originalname=%originalname:~1%"
GOTO subsloop
Your problem is that the delims
strin is not a string, but a set of characters which are the delimiters, so your delimiters are X
, Y
and Z
, not the string XYZ
.
By including the filemask *%targetstring%*.%requiredextension%
, the for
will only select filenames that contain XYZ
and end pdf
. This avoids the clutter of names that do not fit the mask.
However, the match is case-insensitive, so it will detect xyz
for instance.
So, detect the required string. findstr
sets errorlevel to 0
if the string is found. IF ERRORLEVEL 1
means IF ERRORLEVEL 1 OR GREATER
.
If the string is found, then the new name is generated by simply accumulating each character from the beginning of original name until the first 3 characters of the remaining string in original is the required upper-case string. Think in terms of "zelxyzloXYZ World.pdf" where the string xyz
appears before the XYZ
Then try to rename. Note that the REN
command is disarmed and simply echo
ed for verification. Remove the echo
to activate (after verification, naturally).