Using Windows ren command to remove ! from filename - batch-file

I have a bunch of files that are in the following format
filename-!#.ext
where # is an incrementing number to prevent collisions. I would like to just remove the ! from the filename so it reads as
filename-#.ext
It seems like I could do this with the ren command and wildcards but having a hard time getting it to work. I tried running this:
ren *!?.ext *?.ext
My thought is the * should match the filename- part, then !, then the ? for the numeric. However, the resulting file is named this:
filename-!#.ext.ext
and I can't quite figure out why.
The filename part can be dynamic, but won't contain any !'s. Any ideas?

I think you'll have to resort to a small batch file for this:
#echo off
setlocal
for %%a in (*!*.ext) do call :remove "%%~a"
goto :eof
:remove
set "FROM=%~1"
set "TO=%FROM:!=%"
ren "%FROM%" "%TO%"
goto :eof
The above will – for all files containing an exclamation mark and extension ext – call the remove function. This takes the original name (FROM) and determines the new name (TO) by using the %var:find=replace% syntax to remove any exclamation mark (replaces it with an empty string).
Notes
You cannot use find/replace with either %%a or %0 type variables, so you have to assign it to a named variable first.
I originally tried doing this "inline" with the for statement (e.g. for ... ( ending )) but to do that, you would have to enable delayed-expansion (because you would need to access a named variable in a loop). However, delayed-expansion uses ! (instead of %) to reference variables and this got in the way of the ! we were trying to remove. There may be a way of doing this, but I haven't found it. Using call will be slightly slower, but not significantly unless you've got thousands of files.
You don't really need to create TO (you could perform the replacement on the ren command-line) but I used it for clarity.
This will work for all files with an exclamation mark: it doesn't check the bit after is numeric.

I think it is not a good idea to let rename select the files, but instead do it with a for-loop and then execute the rename for every file:
for %%F in (*!*) do (
set "nxF=%%~nxF"
call ren "%%nxF%%" "%%nxF:!=%%"
)
You really have to use call here instead of delayedExpansion, because delayedExpansion would destroy the rename-arguments (because they contain !)

Based upon your provided information and just for the sake of offering something a little different, you could let delayed expansion do the work for you, (as it will remove the unwanted exclamation mark for you).!
#For %%A In ("*-!?.ext") Do #Set "_=%%A" & SetLocal EnableDelayedExpansion & Ren "!_!" "%%A" & EndLocal
You could probably also do it with a nested for loop:
#For %%A In ("*-!?.ext") Do #For /F "Tokens=1*Delims=!" %%B In ("%%A") Do #Ren "%%A" "%%B%%C"
…and from the Command Prompt:
For %A In ("*-!?.ext") Do #For /F "Tokens=1*Delims=!" %B In ("%A") Do #Ren "%A" "%B%C"

Related

Batch Script - Rename files, removing a variable prefix and suffix

I have several files in a folder with names like
"prefix (S-N 12RE3123) suffix.pdf"
I would like to rename them to just "12RE3123.pdf", removing the prefix and suffix. The issue is that both the prefix and suffix are of varying lengths/ characters, so I cannot just rename by removing the first/last xx characters, but have to use the only commonality where only the characters inside the parenthesis following "S-N" are those to be kept.
There is one special case where a few of the serial numbers are named as WD-12RE3123, and I need to remove the WD- as well. If I had to do it manually, there aren't a lot of them like that so it wouldn't be the end of the world, but having it automated would be nice. I thought of maybe doing an if statement after the removal of prefix/suffix to check if the first 3 characters match WD- then remove those if true, but I am not sure on the implementation.
I am still a novice in batch or vbscript, so I was wondering if this can be done in either of those. Batch has the method "REN" to rename files, but since the final name depends upon what the current name is I am not sure how to set up delimiters or how to approach this.
Any assistance would be greatly appreciated.
Here is a simple solution for your request. It relies on the following facts:
the prefix portion does not contain any parenthesis ( or ) on its own (suffix might though);
the serial number does not contain any parentheses ( or ) on its own;
there are no duplicate serial numbers, also with respect to removal of potentional WD- strings;
This is the code (after having tested the code on the files in your target folder, you need to remove the upper-case ECHO command to actually rename the files):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem Set constants here:
set "LOCATION=.\test"
set "PATTERN=*.pdf"
set STRINGS="S-N " "WD-"
cd /D "%LOCATION%"
for /F "eol=| delims=" %%K in ('dir /B "%PATTERN%"') do (
for /F "eol=| tokens=2 delims=()" %%L in ("%%~nK") do (
set "NAME=%%L"
setlocal EnableDelayedExpansion
for %%J in (%STRINGS%) do (
if "!NAME!"=="%%~J!NAME:*%%~J=!" (
set "NAME=!NAME:*%%~J=!"
)
)
ECHO ren "%%~fK" "!NAME!%%~xK"
endlocal
)
)
endlocal
exit /B
How it works:
the first section specifies the folder location, the file pattern and the strings to remove (after having extracted the portion within ()); adapt the values to your needs;
there are several nested for loops; the outermost one simply iterates all the files matching the given pattern (it basically reads and parses the output of a dir command applied on the given files and loops through the returned items; opposed to a standard for loop, this for /F method ensures that the entire folder is read before the loop starts iterating, which is necessary when modifying the enumerated folder content like we do here by renaming files; see also this thread about that issue);
the next for /F loop extracts the file name portion of interest, that is everything between the first pair of parenthesis, and stores it in variable NAME; this loop iterates once only per file;
the is another for loop which walks though all items in the STRINGS variable;
the if clause checks whether the current item of STRINGS occurs at the very beginning of the NAME value; if so, it is removed, otherwise not; this is just a safety query because perhaps a serial number might also contain a given STRINGS item in the middle or at the end (for instance, 123-WD-45A);
at this point, the renaming is performed (after having removed ECHO, of course);
the toggling of delayed expansion is intended to avoid trouble with some special characters in the file names;
And here is another script that uses a more complex method for extracting the interesting parts of the file names. It relies on the following facts:
there occurs only a single substring (S-NSPACE in the file name
the serial number is followed by a );
the serial number does not contain any parentheses ( or ) on its own;
there are no duplicate serial numbers, also with respect to removal of potentional WD- strings;
The code looks like this:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem Set constants here:
set "LOCATION=.\test"
set "PATTERN=*.pdf"
set "FILTER=(S-N [^()][^()]*)"
set "IDENTIFYER=S-N "
set STRINGS="WD-"
cd /D "%LOCATION%"
for /F "eol=| delims=" %%K in ('
dir /B "%PATTERN%" ^| findstr /I /R /C:"%FILTER%"
') do (
set "NAME=%%K"
setlocal EnableDelayedExpansion
set "NAME=!NAME:*(%IDENTIFYER%=!"
for /F "eol=| tokens=1 delims=)" %%L in ("!NAME!") do (
setlocal DisableDelayedExpansion
set "NAME=%%L"
setlocal EnableDelayedExpansion
for %%J in (%STRINGS%) do (
if "!NAME!"=="%%~J!NAME:*%%~J=!" (
set "NAME=!NAME:*%%~J=!"
)
)
ECHO ren "%%~fK" "!NAME!%%~xK"
endlocal
endlocal
)
endlocal
)
endlocal
exit /B
Basically, this script works similar to the above one, with a few deviations:
the first section specifies a file name filter and the serial number identifyer (S-N) in addition;
the dir command in the outermost for loop is piped into a findstr command to filter out files not containing (S-N and ) and a string (not containing (/)) in between already at the beginning;
the part (S-N and everything before is removed, the result is stored in NAME;
the next for /F loop extracts everything up to but not including the first ) from the NAME value, which constitutes the file name portion of interest; this loop iterates once only per file;
the is another for loop which walks though all items in the STRINGS variable, which does not contain the S-N portion here as this has already been removed before; the rest in this loop is the same as in the above script;
at this point, the renaming is performed (after having removed ECHO, of course);
#ECHO Off
SETLOCAL
SET "sourcedir=U:\sourcedir"
FOR /f "delims=" %%a IN (
'dir /b /a-d "%sourcedir%\*(*)*.*" '
) DO (
FOR /f "tokens=1,2delims=()" %%c IN ("%%a") DO (
FOR /f "tokens=1-3delims=- " %%m IN ("%%d") DO (
IF "%%o"=="" (ECHO(REN "%sourcedir%\%%a" "%%n%%~xa"
) ELSE (
ECHO(REN "%sourcedir%\%%a" "%%o%%~xa"
)
)
)
)
GOTO :EOF
You would need to change the setting of sourcedir to suit your circumstances.
The required REN commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(REN to REN to actually rename the files.
Apply each filename matching the mask "()." to %%a
Tokenise using ( and ) as delimiters so that the parenthesised portion is in %%d
Re-tokenise using - and as delimiters so that the required number is in %%o for S-N patterns and %%n for WD- pattern.
Show the rename line.
Note: this should work provided the prefix portion does not contain parentheses and the parenthesised portion is exactly as described.
Magoo and aschipfl both provided good pure batch solutions.
I find development of custom batch scripts for every complex renaming task to be tedious, which is why I wrote JREN.BAT - a regex find/replace renaming utility. JREN.BAT is pure script (hybrid JScript/batch) that runs natively on any Windows machine from XP onward. Full documentation is available from the command line via jren /?, or use jren /?? for paged help.
With JREN.BAT, the solution is as simple as (untested):
jren "^.*\((?:S-N (?:WD-)?)(.+?)\).*" "$1.jpg" /fm *.jpg
If I got the regex wrong, it can easily be fixed. You should probably add the /T option to run the command in test mode first, and then remove it when everything looks good.
If you put the command in a batch script, then you must use CALL JREN.

Beginner Questions (Formatting, "Filepath was unexpected at this time")

I am new to batch files and I only need a very simple thing done.
I would like a batch file to take a text file which is a list of filepaths --
Filelist.txt
Begin File>>
O:\X\Y\Z\Board BOM Rev 4.xls >>Files
O:\X\Y\U >>Entire filepaths
< End File
and then copy the files (not the names of the files, to clarify) to a given location.
Say that the batch file is in O:\X\Y\Z (And so is the text file), and I would like to copy all of those files to that folder. I have tried to use this code
#echo off
set input="O:\X\Y\Z\Filelist.txt"
set dest=%cd%
for /f %%i in "input" do xcopy "%%i" %dest%\ /S
To do what I need to do, but I get the aforementioned error. I have done very little with batch files, so corrections with explanations would be much appreciated!
Thanks!
First, to use your variable "input" write %input%
Second, for the correct format of for you have to put parantheses around it:
for /f %%i in ("%input%") do xcopy "%%i" %dest%\ /S
1st. (input)
In the first set you have double-quoted wrong the value, that formatting is for extraordinary cases, not this time.
2nd. (dest)
You need to ensure to double-quote every "variable=value" (at least when they are string variables and when can contains spaces)
but anyways you can simplify the code a little to don't depend on that dest variable,
3rd. (For)
The dest variable inside for has the same quote problem, also you are typing incorrectly the input variable, and for syntax can be improved to prevent future errors by agroupping the commands using the agroupation operators ().
Here is the code:
#echo off
set "input=O:\X\Y\Z\Filelist.txt"
REM set "dest=%cd%"
for /f "Usebackq Delims=" %%i in ("%input%") do (
xcopy "%%i" ".\" /S
)
PS: Forgive my English.

Determine filename length from a batch file

I need to work two things into a .bat file I am working on for a little project. First things first, I have to know if any filename contained into the same folder (recursively) I launch my .bat in is any longer than 100 characters. If so, I need to make it 92 characters long and keep the extensions.
For example, I have this filename:
IncrediblyLongFileNameIAmSorryForThisItLooksLikeSomeDamnSpamJesusIAintEvenCloseTo100yetalmostwaitforitYEAH.omg
The above filename is 110 characters. I need to keep the extension, therefore the program should rename the file as this:
IncrediblyLongFileNameIAmSorryForThisItLooksLikeSomeDamnSpamJesusIAintEvenCloseTo100yetalmos.omg
So far, my main problem is that I don't know how to work with filename strings in batch. I used this code:
#echo off & setlocal enableextensions
FOR /R %%i IN (*.*) DO (
ECHO %%~nxi
FOR /f "delims=:" %%a in ('
^(echo."%%~nxi"^& echo.^)^|findstr /o .'
) DO set lenght=%%a-5
echo The length of "%%~nxi" is %lenght%
)
endlocal & goto :EOF
But I can't SET inside a FOR, and it can't do basic math either (i.e. it can't do the -5 operation).
The second thing, which I believe should be easier once the first one is done, is simply to compare all the filenames in the folder (recursive, once again) and make sure no filenames are the same. If the program finds any filenames that are the same, the second occurrence should be renamed to add something like l1l at the end. (I can't use parentheses here, therefore I use two ls instead to cover the number.) The only thing you need to take care of is the file extensions, because I can't add anything after the file extensions, lest they become unusable.
Can anyone offer explanations for how to accomplish this? I would really like to be able to work this out myself, but I simply lack experience in batch programming.
#ECHO OFF
SETLOCAL
SET "sourcedir=c:\sourcedir"
SET "tempfile=%temp%\##fn##.92"
ECHO ::>"%tempfile%"
FOR /f "delims=" %%a IN (
'dir /s /b /a-d "%sourcedir%\*" '
) DO (
SET "fullname=%%a"
SET "name=%%~na"
SET "ext=%%~xa"
CALL :chgname
)
del "%tempfile%"
GOTO :EOF
:chgname
:: Proposed new name part - first 92 characters of existing name
:: also prepare for adding modifier
SET "newname=%name:~0,92%"
SET /a modifier=0
:modl
:: See whether this name has already been found
ECHO %newname%%ext%|FINDSTR /b /e /i /g:"%tempfile%" >NUL
IF ERRORLEVEL 1 GOTO makechange
:: existing name - modify it
SET "newname=%name:~0,92%#%modifier%#"
SET /a modifier+=1
GOTO modl
:makechange
IF "%name%" NEQ "%newname%" ECHO REN "%fullname%" "%newname%%ext%"
>>"%tempfile%" ECHO %newname%%ext%
GOTO :eof
Reasonably simple problem.
Get a directory-list in basic form (full-filename only) and apply the full filename, name part and extension part to appropriately-named variables.
Manipulate the filename to a new name consisting of the first 92 characters of the original name part. Anticipate the need to modify this new name by establishing a modifier to optionally be applied.
See whether the proposed new name already exists in the temporary file of NEW names already processed. If not found on that file, safe to rename (if required) and record name used.
If the filename has already been used, modify it to the original first 92+ "#anumber#", increment the modifier in anticipation and try again.
Only two comments required further - first, I used # rather than ! because ! has a special meaning to batch. Second, writing :: to the tempfile (the name of the tempfile is irrelevant - I chose one that's unlikely to exist...) means that findstr doesn't complain because the file is empty, but :: can't possibly be a real filename.
The /b /e /i options to findstr mean that the name echoed in must exactly match a line (matches both /b - begin and /e - end) but /i - case is irrelevant.

Delayed expansion doesn't work inside delayed expansion

I've got a script that does everything I expect it to do, apart from one line.
I've done similar before, but I can't get this one to work.
The code I've got is here
#echo off
SETLOCAL ENABLEDELAYEDEXPANSION
::Set Path to be folder of Sage Files
SET PATH=C:\Welcome\Progs\SitesDataSetups\GeorgeYarmouth
::set date variables
for /f "tokens=1" %%i in ('date /t') do set thedate=%%i
set mm=%thedate:~3,2%
set dd=%thedate:~0,2%
set yyyy=%thedate:~6,4%
::Set T_DAY variable to date in ddmmyy format
set T_DAY=%dd%%mm%%yyyy:~2%
c:
cd\
cd %path%
for /f "usebackq tokens=* delims= " %%P in (`dir sage*.csv /od /b`) do (
set SAGE=%%P
set SAGE2=!SAGE:~0,8!_EDITED
set EODNUM=!SAGE:~4,4!
for /f "tokens=* delims= " %%A in (%%P) do (
echo %EODNUM%
set S=%%A
***This line is the problem***
set S=!S:%T_DAY%=%EODNUM%!
echo.!S! >> %PATH%\TEST\!SAGE2!.csv
)
)
endlocal
I was expecting that is would take each line of the csv file and replace it with itself, except with a string replace of the current date with the variable EODNUM (which it does... only the variable is expanded before it is set, so is nothing)... The delayed expansion should solve this, but I can use this line of code
set S=!S:%T_DAY%=!EODNUM!!
because I think its too many !'s for CMD.
Am I missing something, or is there a better way to code this?? (I'm not a programmer of any kind, and most of the code I write comes from trial and error, and 'borrowing' from other scripts, so this may be a very messy way to do this).
Transfer the the value of !EODNUM! to a FOR variable, and then use your FOR variable as the replace string.
echo !EODNUM!
set "S=%%A"
for /f "delims=" %%E in ("!EODNUM!") do set "S=!S:%T_DAY%=%%E!"
echo.!S!>> %PATH%\TEST\!SAGE2!.csv
By way of explanation...
CMD reads (and does env var substitution), parses, and executes one top-level command at a time.
In your example, it reads the "for /f..." command all at once parsing and performing %var% substitution.
Once this is complete, it then executes the for loop, performing delayed !var! substitution.
Unfortunately, !var! substitution is not a do-substitution-until-none-left. This makes it hard (as in the answerer's solution) to perform the substitution into the !var:src=dst! value.
You will need a way that during execution you can get guaranteed substitution. This requires a for-statement, or something that involves reading and %var% substituting again. One way of doing this is to use the CALL :LABEL form where you can call to a specific label in your .cmd file and have this section do what you want:
...
call :GenS
...
and then:
:GenS
set S=!S:%T_DAY%=%EODNUM%!
goto :eof
BTW: I'm perplexed that you didn't notice the ECHO %EODNUM% not working in the loop as during the reading of the for loop all %var% substitutions are made.

Filenames renaming using .bat?

Heythere.
I need your guys help with file renaming using a .bat file.
I got multiple files that I need to be cropped after specified character number.
For exmample, I want to crop name of the files after their fifth character, this way
filename.exe > filen.exe
anothername.exe > anoth.exe
absolutelydifferentname.exe > absol.exe
And, if possible, I'd like to know how to do the opposite. I mean, leaving the certain # of characters at the end, cropping from the beginning of the filename.
Thank you.
If you want to do it in a batch file, the following should work:
#echo off
setlocal ENABLEDELAYEDEXPANSION
for %%i in (<Directory name here>\*) do (
set filename=%%~ni
ren "%%~i" "!filename:~0,5!%%~xi"
)
endlocal
If you wish to change the number of characters used to construct the final filenames, change the "5" in ren "%%~i" "!filename:~0,5!%%~xi".
To take the last 5 characters try: ren "%%~i" "!filename:~-5!%%~xi"
For all except the first 5 characters: ren "%%~i" "!filename:~5!%%~xi"
The Iridium solution will fail if any file name contains the ! character because expansion of %%i will fail if value contains ! and delayed expansion is enabled. This is easily fixed by toggling delayed expansion on and off within the loop.
The explicit disabling of delayed expansion at the top is normally not needed. But it is possible for delayed expansion to already be enabled at the start of a batch file, so I included it just to be safe.
#echo off
setlocal disableDelayedExpansion
for %%F in ("<Directory name here>\*") do (
set "full=%%F"
set "name=%%~nF"
set "ext=%%~xF"
setlocal enableDelayedExpansion
ren "!full!" "!name:~0,5!!ext!"
endlocal
)
Just incase this is a once-off, or some other manual scenario, have a look at
CKRename

Resources