use * with a batch file to get filenames - batch-file

I am using Windows XP and need to create a batch file to move tok.filename to filename.tok.
This works for one file if I type in fix.bat tok.filename.
set filename=%1
set newname=%filename:~4,45%
ren %1 %newname%.tok
I need to type in fix.bat tok*, but this puts tok* in the filename.
How do I get it to read all the files into the filename one at a time?

Use a for statement.
setlocal enabledelayedexpansion
for %%i in (tok.*) do (
set filename=%%i
set newname=!filename:~4,45!
ren %%i !newname!.tok
)
Enabling the delayed expansion makes it so that the variables are evaluated at the time they are used.
Alternatively, since you already have a batch file that works, you could write another batch file that uses the for statement, which calls your working batch file -- like this:
for %%i in (tok.*) do call fix.bat %%i
Or you could run it directly from the command line like this:
for %i in (tok.*) do call fix.bat %i

No need for a batch script
for %F in (tok.*) do for /f "delims=." %X in ("%~xF") do ren "%F" "%X.%~nF"
Double up the percents if used within a batch script.
The above may not give the desired result if more than one . appears in a file name.
For example tok.part1.part2 would become part2.tok.part1.
If you want part1.part2.tok instead, then you can use the following simple script.
#echo off
ren tok.* .*?.tok
for %%F in (.*.tok) do for /f "tokens=* delims=." %%A in ("%%F") do ren "%%F" "%%A"
For an explanation of how that 1st REN works in the 2nd script, see How does the Windows RENAME command interpret wildcards?
I suppose an esoteric argument could be made that a name like tok..name would become name.tok when it should be .name.tok. That could be fixed by using:
#echo off
setlocal disableDelayedExpansion
ren tok.* .*?.tok
for %%F in (.*.tok) do (
set "name=%%F"
setlocal enableDelayedExpansion
ren "!name!" "!name:~1!"
endlocal
)
The nice thing about the solutions above is they don't rely on the length of "tok". A string value of any length could be substituted for "tok" and the code works just as well.
The James L solution works well for a constant length. That solution can be improved a bit.
The 2nd argument to the substring operation is not needed. Removing it lets the solution work no matter how long the full original name is.
The code will fail if a name has an ! in it. Fixed by toggling delayed expansion on and off.
The filenames should be quoted in the REN statement in case of spaces or special characters
.
#echo off
setlocal disableDelayedExpansion
for %%i in (tok.*) do (
set "filename=%%i"
setlocal enableDelayedExpansion
ren "!filename!" "!filename:~4!.tok"
endlocal
)

you can also do the same without delayed expansion (which is slow).
Also, if there is only one dot, without explicitly using lengths of name parts.
%~n expands to filename without extension, and %~x to dot with extension (so I remove dot with :~1).
Also, using %* in for, you may pass arbitrary number of arguments.
And even more. If you add /R bewteen for and %%I (you'll get for /R %%I), it will recurse into subdirs also.
Whole batch file:
for %%I IN (%*) do call :Rename "%~I"
exit /b
:Rename
set newname=%~x1
ren %1 "%~dp1%newname:~1%.%~n1"
exit /b

Related

Trying use a batch file to delete filenames appended with numbers

I"m trying to delete these files with numbers appended to them using a for loop in a Windows batch file.
My problem is that I can't seem to build the filename strings using the for loop, despite several attempts at doing this.
Below is the snippet of my code that I"m trying to run with. As you can see, I've got 4 files named file0.txt, file1.txt, file2.txt and file3.txt in some nested folder, and I'm trying to delete them using a for loop
Ideally I want to be able to be able to set the limit of the for loop depending on how many files there are. And then I'd want to change the file extension from .txt to whatever to delete other files except the ones I want to keep.
Any help would be appreciated!! Here's the code:
setlocal enabledelayedexpansion
set num=3
set /a forLoopLimit=%num%-1
cd folder
FOR /L %%x IN (0,1,%forLoopLimit%) DO (
setlocal enabledelayedexpansion
echo %x
set y=%x
set filename=file%y%.txt
del %filename% /f /q
echo %filename%
)
cd ..
setlocal enabledelayedexpansion
set num=3
set /a forLoopLimit=%num%-1
cd folder
FOR /L %%x IN (0,1,%forLoopLimit%) DO (
echo %%x
set filename=file%y%.txt
ECHO del file%%x /f /q
echo file%%x
)
cd ..
Problems:
To refer to the metavariable (Loop-control variable) x, you need %%x, not %x within a batch file.
setlocal is not a switch. Each time it is used, it establishes a new frame which is terminated by endlocal or reaching end-of-file.
If you are using delayededexpansion then you need to refer to the variable you are changing using !var!, not %var%. !var! means the changed value, %var% means the value of the variable as it was when the for keyword was encountered.
You don't need to use y in your application. %%x is actually a string, but it will be a numeric string, so it can be used in calculations.
The del command is simply echoed above to allow the command to be displayed - in case there's a code error which might delete unexpectedly.
Basically, you fell into the delayed expansion trap (you enabled delayed expansion, but you didn't use it).
But your task can be done without those variables that need delayed expansion. You can use %%x directly and define the rest outside the loop:
#echo off
set num=3
set /a forLoopLimit=%num%-1
set "filebase=file"
pushd folder
FOR /L %%x IN (0,1,%forLoopLimit%) DO (
ECHO del "%filebase%%%x.txt" /f /q
)
popd
Like Magoo, I disarmed the del command by just echoing it. If the output is what you expect, just remove the ECHO.

How to find and replace part of filenames from list

I have a list of files that I need to rename at the same part of each file, with different values.
Example:
BL_1402B103_abc.wav > BL_C1234-1_abc.wav
BL_15489B59_abc.wav > BL_C1234-5_abc.wav
So in the first example above I want to replace the 1402B103 with C1234-1 all the files are the same length and the sections I want to replace are separated by "_".
I have some code for finding/replacing parts of a filename but I need to do this for hundreds of files - is there a way to pull Pattern= & Replace= as variables from a csv/list and run as a batch?
Setlocal enabledelayedexpansion
Set "Pattern=1402B103"
Set "Replace=C1234-1"
For %%f in (*.wav) Do (
Set "File=%%~f"
Ren "%%f" "!File:%Pattern%=%Replace%!"
)
You could create a csv file and add your search/replace strings:
myfile.csv
1402B103,C1234-1
15489B59,C1234-5
etc,etc
The batch file, myrename.cmd
#echo off
setlocal enabledelayedexpansion
for /f "tokens=1,* delims=," %%i in (myfile.csv) do (
set "search=%%i"
set "replace=%%j"
call :fix
)
exit /b
:fix
for %%a in (*!search!*.wav) do (
set "file=%%a"
set "file=!file:%search%=%replace%!!"
echo ren "%%~fa" "!file!"
)
It will seatch for each string in the csv file, split by comma assign the first meta variable to the search variable and the second to the replace variable. Then we simply do the replace for each by calling that procedure.
Note!! in this instance I used echo before ren for testing results. Only once you are happy with your results should you remove echo to perform the actual command.
I would do such a multi-rename operation of files using shareware Total Commander with its built-in multi-rename tool which has a every easy to use graphical user interface for such tasks making it possible to review the new names of the files before executing the rename operation. This file rename operation could be done with Total Commander nearly complete using only some mouse clicks, just C1234- need to be typed on keyboard. And Total Commander supports even an undo if the rename operation fails for some reason.
Let us assume C1234- in new file name is a fixed sequence of characters and 1 and 5 is a number incremented by one on each renamed each file.
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "FileNumber=1"
for /F "eol=| delims=" %%I in ('dir BL_????????_abc.wav /A-D-H /B /ON 2^>nul') do (
move /Y "%%I" "BL_C1234-!FileNumber!_abc%%~xI" >nul
set /A FileNumber+=1
)
endlocal
This solution works for the example.
But what about string left to first underscore and string right to second underscore vary from file name to file name?
In this case the following batch file could be the right solution:
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "FileNumber=1"
for /F "eol=| delims=" %%I in ('dir *_????????_*.wav /A-D-H /B /ON 2^>nul') do (
for /F "eol=| tokens=1,2* delims=_" %%A in ("%%~nxI") do (
move /Y "%%I" "%%A_C1234-!FileNumber!_%%C" >nul
)
set /A FileNumber+=1
)
endlocal
The command MOVE with option /Y is used instead of command REN to make the file rename even on a file with that name is already existing. Total Commander would inform the user about such an issue on renaming files with other files with new name already existing.
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.
dir /?
echo /?
endlocal /?
for /?
move /?
set /?
setlocal /?
Read the Microsoft article 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 with using a separate command process started in background.
Batch solutions just make it hard to know what to do.
Batch is far too unreadable for my liking. %% and ! and !! in weird places. Bah.
You are better off making a C# console app that gives you 2 prompts and just does it, like I did.
In any case, what is being demonstrated is, for each file, you grab that filename as a string variable, and do all the string replacements in that string variable. Then you rename the file, or in C# it's MoveTo(newPath), the resulting string variable.

Replacing a string in a bunch of text files in a windows folder

Need help ! Working on a batch file which will replace a set of characters from a bunch of text files in a folder. I have found the code which will do that. But it does for only one file. IS there a way where it can do it for all the files in the folder. There a total of 1000 files inside the folder. I am using a Windows 7 OS. Attaching the code I found where it does for one file.
Thanks
Harry
#echo off
setlocal enabledelayedexpansion
set INTEXTFILE=Replace_string.txt
set OUTTEXTFILE=test_out.txt
set SEARCHTEXT=Apple
set REPLACETEXT=Mango
set SEARCHTEXT=Cat
set REPLACETEXT=Dog
set OUTPUTLINE=
for /f "tokens=1,* delims=¶" %%A in ( '"findstr /n ^^ %INTEXTFILE%"') do (
SET string=%%A
for /f "delims=: tokens=1,*" %%a in ("!string!") do set "string=%%b"
if "!string!" == "" (
echo.>>%OUTTEXTFILE%
) else (
SET modified=!string:%SEARCHTEXT%=%REPLACETEXT%!
echo !modified! >> %OUTTEXTFILE%
)
)
del %INTEXTFILE%
rename %OUTTEXTFILE% %INTEXTFILE%
If you want a pure batch solution, then the simplest thing to do is to encapsulate the code in a subroutine that takes the name of the file as an argument, and call that routine from within a FOR loop that iterates the file names. This is a general approach that can be used for any code that you want to run iteratively
against files in a folder.
The end result of your code does not create or rename any folders, so a simple FOR is safe to use. But if your code creates or renames folders, then a FOR loop could also process the newly created or renamed files. This can be solved by using FOR /F with the DIR /B command instead.
Note - I eliminated dead (unused) variables from the code.
#echo off
setlocal enabledelayedexpansion
pushd "c:\path\to\your\folder\containing\files\to\modify"
for %%F in (*.txt) do call :replace "%%F"
exit /b
:replace
set INTEXTFILE=%1
set OUTTEXTFILE=test_out.txt
set SEARCHTEXT=Cat
set REPLACETEXT=Dog
for /f "tokens=1,* delims=¶" %%A in ( '"findstr /n ^^ %INTEXTFILE%"') do (
SET string=%%A
for /f "delims=: tokens=1,*" %%a in ("!string!") do set "string=%%b"
if "!string!" == "" (
echo.>>%OUTTEXTFILE%
) else (
SET modified=!string:%SEARCHTEXT%=%REPLACETEXT%!
echo !modified! >> %OUTTEXTFILE%
)
)
del %INTEXTFILE%
rename %OUTTEXTFILE% %INTEXTFILE%
exit /b
But there are many limitations and inefficiencies with the code, with lots of room for improvement.
Limitations:
Input and output lines must be a bit less than 8191 bytes long.
The search ignores case
The search string cannot contain = or begin with ~, * or !
The replace string cannot contain !
Lines containing ! will be corrupted because delayed expansion is enabled when %%A is expanded. This can be solved by strategically toggling delayed expansion on and off within the loop(s).
Leading : will be stripped from all lines because consecutive delimiter characters are treated as a single delimiter.
The replacement will be corrupted if the search term contains %%a or %%b or %%A. This can be avoided by transferring the search and replacement terms to FOR variables.
Certain characters within the search and/or replacement terms could cause problems or require complex escape sequences. This can be simplified by getting the desired strings in environment variables (which may still require escape sequences) and then using delayed expansion and FOR /F to transfer the values to FOR variables.
There are obscure situations where ECHO. can fail. The only safe variant that is guaranteed to work is ECHO(.
A non empty line could become empty after replacement if the replacement string is empty, and the empty line will not be output properly because neither ECHO. nor ECHO( was used.
Inefficiencies / other issues
Redirection is performed for each line of output, which is slow. Better (faster) to redirect once outside the loop.
The DEL/RENAME pair can be replaced by a single MOVE command
CALL is relatively slow. Best to minimize use of CALL if you want the fastest possible solution. But sometimes it cannot be avoided.
I prefer to have my temp file name to be a derivative of the original name. I typically append a .new extension to the original name, so "original.txt" becomes "original.txt.new"
Below is highly optimized code that addresses all but the first 4 points above. It is about as robust and efficient as pure batch can get if you want to use FOR /F to read the file.
#echo off
setlocal disableDelayedExpansion
pushd "c:\path\to\your\folder\containing\files\to\modify"
set "find=Cat"
set "repl=Dog"
setlocal enableDelayedExpansion
for /f delims^=^ eol^= %%S in ("!find!") do (
for /f delims^=^ eol^= %%R in ("!repl!") do (
endlocal
for %%F in (*.txt) do (
for /f "delims=" %%L in ('findstr /n "^" "%%F"') do (
set "ln=%%L"
setlocal enableDelayedExpansion
set "ln=!ln:*:=!"
if defined ln set "ln=!ln:%%S=%%R!"
echo(!ln!
endlocal
)
) >"%%F.new" & move /y "%%F.new" "%%F" >nul
)
)
popd
The above required a lot of experience and arcane knowledge to develop. Even after all the work, it still has the following limitations.
Input and output lines must be a bit less than 8191 bytes long.
The search ignores case
The search string cannot contain = or begin with ~, * or !
The replace string cannot contain !
Removing those limitations would require a ridiculous amount of slow and even more impenetrable code. Hardly worth it.
That is why I have abandoned using pure batch for text processing, and I developed JREPL.BAT to provide powerful regular expression text processing capabilities to the Windows command environment. JREPL is pure script (hybrid JScript/batch) that runs natively on any Windows machine from XP onward.
JREPL makes the solution so simple, it can easily be run directly on the command line, without any batch file.
pushd "c:\your\path\with\files\to\be\modified"
for %F in (*.txt) do call jrepl "Cat" "Dog" /l /f "%F" /o -
popd
The search is case sensitive. You can add the /i option if you want the search to ignore case. Change the %F to %%F if you use the command within a batch script.
Since all you need to do is replace text in an existing file, use a tool like FART which is specifically designed to do this:
FART -r "C:\Data\Directory\*.txt" "OldText" "NewText"
The -r switch says to process all txt files in provided directory and subfolders.
Note that you can add the -i switch to ignore the case when searching.

Rename multiple files in a directory using batch script

I have about 1000 images and they have name like "IMG-12223". I want to rename them to 1 2 3 4 ... 1000. How can I do that. I have written a batch script which list the files but I don't know how to rename each file. e.g. rename first image with name "IMG-12223" to 1 , second image with name "IMG-23441" to 2 and so on ...
for /r %%i in (*) do (
echo %c%
)
Here's the script. Just put the script in your folder and run it.
#echo off & setlocal EnableDelayedExpansion
set a=1
for /f "delims=" %%i in ('dir /b *') do (
if not "%%~nxi"=="%~nx0" (
ren "%%i" "!a!"
set /a a+=1
)
)
If you want to keep the extensions, i.e. rename "IMG-12223.jpg", "IMG-12224.jpg", etc to "1.jpg", "2.jpg", etc, you may use the following script.
#echo off & setlocal EnableDelayedExpansion
set a=1
for /f "delims=" %%i in ('dir /b *.jpg') do (
ren "%%i" "!a!.jpg"
set /a a+=1
)
[Update] Here're explanations for the lines mentioned in Jack's comment.
setlocal EnableDelayedExpansion
In general, we want the variable a to be delayed expansion when it's executed but not the line is read. Without it, the variable a cannot get its increased value but always 1.
For the detail of EnableDelayedExpansion, please refer to the answer https://stackoverflow.com/a/18464353/2749114.
for /f "delims=" %%i in ('dir /b *.jpg')
Here dir with /b option, lists only file names of all jpg files.
The for loop traverses and renames all jpg files.
For the delims option, since the default delimiter character is a space, without the option delims=, it fails with the image files with spaces in the file names. I.E. for an image file named "img with spaces.jpg", without the option, the value of %%i is "img" but not the whole name "img with spaces.jpg", which is incorrect.
For for loop, please refer to the page http://ss64.com/nt/for_f.html.
if not "%%~ni"=="%~n0"
I have change it to if not "%%~nxi"=="%~nx0" to be more accurate. And the codes attached have been updated.
It's actually used to avoid to rename the bat file itself. If we limit the renaming only upon "jpg" files, then the line is not needed.
%%~nxi is the file name with extension for each file traversed. And %~nx0 is the running bat file with extension. For details, please refer to the page DOS BAT file equivalent to Unix basename command?.
There is no need for a batch script. A simple one liner from the command line can do the job :-)
I use DIR /B to generate the list of files, piped to FINDSTR to number the files, all enclosed withn FOR /F to parse the result and perform the rename.
for /f "delims=: tokens=1*" %A in ('dir /b *.jpg^|findstr /n "^"') do #ren "%B" "%A%~xB"
Double the percents if you want to put the command in a batch script.
Try this, you have pair of namevalues in a text file then loop values and do the magic. Namevalues are separated by empty spaces. This allows you to map old->new filenames accordingly. Or you keep idx+1 counter and use it for new filenames.
keyvalue.bat
#echo off
set idx=0
for /F "tokens=1,2" %%A in (keyvalue.txt) do call :PROCESS "%%A" "%%B"
GOTO :END
:PROCESS
set var1=%~1
set var2=%~2
set /A idx=%idx%+1
echo %var1% goes to %var2% (%idx%)
GOTO :EOF
:END
pause
keyvalue.txt
file888.dat newfile1.dat
file333.dat newfile2.dat
file9.dat newfile3.dat
file01.dat newfile4.dat

Renaming multiples files with .bat

I've been looking for a .bat file in Windows 7 that given a name and a file directory, renames all of the files in that directory to the name that the user pass concatenated to a number that goes from 0 to 9.
For example:
Let's say that i have a directory with two files a.txt and b.txt, i want to change their names to documentX.txt (X being a number from 0 to 9), so at the end the script will change the names to document1.txt and document2.txt
i have tried something like this but without sucess:
#echo off
#set a=1
#set pref=hola
for /D %%f in (C:\*.txt)
do
rename *.txt %pref%%a%
#set /A a = %a%+1
The FOR command cannot span multiple lines unless you use parentheses or line continuation ^. The first opening parenthesis must be on the same line as the IN and the 2nd on the same line as the DO.
You cannot use normal expansion within a parenthesised block of code that also sets the value because the expansion occurs at parse time and the entire block is parsed at once. So the value will be the value that existed before you set it! The solution is to use delayed expansion. That occurs when the line is executed. It must be enabled with setlocal enableDelayedExpansion.
Delayed expansion causes a problem because ! is valid within a file name and FOR variable expansion will be corrupted if it contains ! while delayed expansion is enabled. The solution is to toggle delayed expansion on and off within the loop.
You want a simple FOR without any options. The /D option looks for directories instead of files.
You do not need to expand numeric variables within a SET /A statement.
One last thing - no need for # after you use ECHO OFF.
#echo off
setlocal disableDelayedExpansion
set "n=0"
set "pref=document"
for %%F in (c:\*.txt) do (
set "file=%%F"
set /a n+=1
setlocal enableDelayedExpansion
ren "!file!" "%pref%!n!.txt"
endlocal
)
There is a simpler way to accomplish the task. Use DIR /B to list all the .txt files and pipe the results to FINSTDR /N "^". The FINDSTR will match all files and will prefix each value with a sequential number followed by a colon. Use FOR /F to parse the result into the number and the file name so you can then RENAME.
#echo off
setlocal
set "pref=document"
for /f "tokens=1* delims=:" %%A in ('dir /b /a-d *.txt^|findstr /n "^"') do (
ren "%%B" "%pref%%%A.txt"
)

Resources