Batch: how do you bulk rename files (getting close)? - file-rename

I've seen people do this in Perl, but I'm wondering if there's a way to do it via batch? It's built into Windows, so I think it would be more useful to know how to do this with a batch script. It doesn't require installing anything onto a computer.
An example input name: myFile_55
An example output pattern: change myFile to picture and reduce the number by 13.
An example output: picture_42.
How would you approach this? I know a batch command to rename:
ren myFile_55 picture_42.
So, if I have a file named renamer.bat, I can add the following:
for /r %%x in (%1) do ren "%%x" %2.
Then I can type this command:
renamer.bat myfile* picture*.
I don't know how to reduce the numbers, though.

You can probably put the original file name through a for loop, and extract the name and numbers, do the math on the numbers, then plug it back in with the new name and number. As long as the filename format is name_number you can use this:
REM Allow for numbers to be iterated within the for-loop i.e. the i - z
SETLOCAL ENABLEDELAYEDEXPANSION
SET i=0
SET z=13
SET newName=picture
SET workDir=C:\Path\To\Files
REM Given that filenames are in the format of 'Name_number', we're going to extract 'number
REM and set it to the i variables, then subtract it by 13, then rename the original file
REM to what the newName_x which if the filename was oldName_23 it would now be newName_10
FOR /r %%X in (%1) do (
FOR /F "tokens=1-2 delims=_" %%A IN ("%%X") DO (
SET i=%%B
SET /A x=!i! - %z%
REM ~dpX refers to the drive and path of the file
REN "%%~dpX\%%A_%%B" "%newName%_!x!"
)
)
EDIT: Edited the REN command to include the drive and path of the original file. Changed from lower case x to upper case X as to not confuse %%~dpX.

Related

Batch script to read numerical part of file and rename with next higher integer

I have a file, such as -
foofile_1.ext
A script should read the numerical part of the file, and then rename the file with the next integer, i.e., after execution, the file name should be
foofile_2.ext
I can do it with a C++ / c application or even in bash but not sure how to write a batch script to perform this rename. The filename before the _ isn't going to change, and _ will aaways appear in the same position within the filename.
I can strip the filename to _, but recognizing the numerical is an implementation I am not familiar with. Once I recognize the numerical, I can increment it and rename the file.
Something to consider is that renaming foofile_1.ext to foofile_2.ext will fail should foofile_2.ext already exist. One way to get around it is to rename in descending numerical order, I posted an answer like that before on SO.
I am however not going to post the same answer here, nor link that answer. I will however show one other method:
#echo off
setlocal enabledelayedexpansion
for /f "tokens=1,*delims=_" %%i in ('dir /b /a-d "*_*.ext"') do (
echo %%~nj | findstr /R /V /C:"[A-Z]">nul && (
set /a numeric=%%~nj+1
ren "%%~i_%%~j" "%%~i_-hld-!numeric!%%~xj"
)
)
for /f "delims=" %%f in ('dir /b /a-d "*_-hld-*.ext"') do (
set "name=%%~f"
ren "%%~f" !name:-hld-=!
)
Considering that your input is as you said and does not contain earlier _'s anywhere. This will just take each file with the *_*.ext format. We split by the _ into two tokened metavaiables (%%i and %%j) We take the numeric value and increment by one, then rejoin %%i which is pre _. This however is where the problem comes when the file you are trying to rename to exists, so for that, first we test if %%~nj does not have Alphabetical characters only, using findstr (not doing special characters in this free code) Secondly we give a temporary addition to the name to prevent name clashing.
Once we are done, we simply do a rename on all the files containing the _-hld- temp inclusion.

Searching for partial path\filename in bat

Ok, so I've been bating (hehe) my head against a wall here.
I am looking for an option/code that would allow me to search for a partial path and/or filename from a .bat script that I would export to an outside file.
Now, "search", "export" and "outside file" is something I am fine with. The part that is giving me a headache is the "partial".
To elaborate.
I am looking for a folder called DATA and a file called userinfo.txt inside DATA.
Those are constant. So the path I have is DATA\userinfo.txt
I am also 99% certain that this folder will be in D:\ but thats not a concern right now. Where ever it is I'll find it.
But I cannot figure out how to look for a partial path\filename for the life of me.
Reason I have specified that DATA\userinfo.txt is a constant is due to other folders ability to be named arbitrarily. So in my below example 01-12-2016 does not have to be named according to that convention. For USA it would most likely be named 12-01-2016. It is also sometimes named 20161201 or 20160112 or on top of all that has a letter prefix such as d01-12-2016. On that note DATA is always DATA, which is why I said DATA is constant in my search. Another thing that will be the same is the grandparent folder. When i say "same" i mean "shared" between the two applications. It does not mean it will always be named "program" as in my example below.
Googling this and using things I know has got me nowhere.
Reason I cannot simply use
where /r d: userinfo.txt
is that that specific command will return hundreds of results as there is a userinfo.txt created for every.single.day the program was running and is stored separately.
Alternatively - if there would be a way to comb trough those hundreds of results and find the matching part that would also resolve my issue.
This however brings up another headache as there is usually more than one program with this exact file.
so in the example of
d:\users\path\program\storage\01-12-2016\userinfo.txt
d:\users\path\program\otherstorage\01-12-2016\userinfo.txt
d:\users\path\program\storage\02-12-2016\userinfo.txt
d:\users\path\program\otherstorage\02-12-2016\userinfo.txt
d:\users\path\program\storage\03-12-2016\userinfo.txt
d:\users\path\program\otherstorage\03-12-2016\userinfo.txt
d:\users\path\program\storage\04-12-2016\userinfo.txt
d:\users\path\program\otherstorage\04-12-2016\userinfo.txt
d:\users\path\program\storage\05-12-2016\userinfo.txt
d:\users\path\program\otherstorage\05-12-2016\userinfo.txt
d:\users\path\program\storage\06-12-2016\userinfo.txt
d:\users\path\program\otherstorage\06-12-2016\userinfo.txt
d:\users\path\program\storage\data\userinfo.txt
d:\users\path\program\otherstorage\data\userinfo.txt
Note: storage, otherstorage, storageother, storage2, storagegh are all arbitrary names as these folders are named accoring to end-user wishes.
I would want to export two separate variables for
d:\users\path\program\storage
and
d:\users\path\program\otherstorage
I would also need to do this for \data\userinfo.txt
So if searching for \data\userinfo.txt it would return
d:\users\path\program\storage\data\userinfo.txt
d:\users\path\program\otherstorage\data\userinfo.txt
I would also want to isolate both
d:\users\path\program\storage
and
d:\users\path\program\otherstorage
and use it as (separate) local variables.
I would need to note that installing/downloading any external scripting tools/aids would not be a suitable solution as I work on a lot of computers, most of which I do not have internet access and/or sufficient permissions for external downloads/installations so anything that is not integrated into the bat and needs to be imported separately is a bad idea.
Also, I am working on Windows XP SP3 but I would need this bat to be able to run on XP SP2, XP SP3, Windows 7, Windows 10, Windows NT, Windows 2000.
Any help would be appreciated.
Please note that
d:\users\path\program
would also be an acceptable variable. In this case I would manually amend the remainder of the path or would rely on end-user (my coworkers) input to complete the path correctly. The last has proven to be a fools errand.
The way that I've been handling it until now is to look for a .exe that I KNOW will be in both folders. This is a part of my code below edited to match the current example.
#echo off
SETLOCAL
echo Program will now look for program.exe and programgh.exe. Please input, when asked, matching part of the path for these files.
echo Example:
echo d:\users\path\program\storage\bin\program.exe
echo d:\users\path\program\otherstorage\bin\programgh.exe
echo In above example matching part is d:\users\path\program so you would enter that when prompted
echo Please do not input the last pathing mark: \ (backslash)
echo -------------searching---------------
::I am exporting errors to nul as I don't want them to be spammed by errors and other data that they would think is their fault
where /r c: program*.exe 2>nul
where /r d: program*.exe 2>nul
where /r e: program*.exe 2>nul
where /r f: program*.exe 2>nul
set /p dualpath="Please enter matching paths for program folder: "
After that I would proceed to work with %dualpath% variable.
As it usually happens (to me at least) most people would just copy the example path without taking a look at what the program has spat out and would be confused as to why the program did not work. Either that or would copy everything up to program.exe and programgh.exe - including the otherstorage\bin\ without noticing that \storage\ and \otherstorage\ do not match.
I think this now covers all the comments or additional questions and clarifies a bit better what I need. Thank you all for help so far and I hope that this is easier to understand.
If a Windows cmd command allows wildcards in a (partially or fully qualified) path then wildcards must be used only in the path leaf (i.e. the last item or container in the path). However, you could apply findstr regex to narrow command output e.g. as follows:
where /r d:\ userinfo.txt | findstr /I "\\storage2*\\data\\userinfo.txt"
above command wold narrow output to paths ending with \storage\data\userinfo.txt and \storage2\data\userinfo.txt
Another example - narrow output to paths ending with \storageX\data\userinfo.txt where X is either nothing or any decimal cipher [0-9]:
dir /B /S d:\userinfo.txt | findstr /I "\\storage[0-9]*\\data\\userinfo.txt"
Put the paths to environment variables (with _var prefix for easier next identification), e.g. _varstorage, _varstorage2, …
#ECHO OFF
SETLOCAL EnableExtensions
for /F "delims=" %%F in ('
dir /B /S "d:\userinfo.txt" ^| findstr /I "\\storage[0-9]*\\data\\userinfo.txt"') do (
for /D %%D in ("%%~dpF..") do (
set "_var%%~nxD=%%~fD"
rem %%~fD path
rem %%~nxD last item in above path
rem _var variable name prefix
)
)
rem show result:
set _var
See also next %%~nxD and %%~D explanation: Command Line arguments (Parameters): Parameter Extensions
If I got your intention right, the following script should do what you want:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=D:\" & rem "D:\", "D:\users",..., or "D:\users\path\program"
set "_FILE=userinfo.txt"
rem // Initialise index:
set /A "INDEX=1"
rem // Search for the specified file in the given root directory:
for /F "delims=" %%F in ('dir /B /S "%_ROOT%\%_FILE%"') do (
rem // Iterate once over the grandparent directory itself:
for /D %%D in ("%%F\..\..") do (
rem // Resolve the path of the grantparent directory;
set "ITEM=%%~fD"
rem // Initialise flag (non-empty means not yet stored):
set "FLAG=#"
rem // Toggle delayed expansion to avoid trouble with exclamation marks:
setlocal EnableDelayedExpansion
rem // Iterate over all currently stored grantparent paths:
for /F "tokens=1,* delims==" %%V in ('2^> nul set $ARRAY[') do (
rem // Clear flag in case current grandparent has already been stored:
if /I "!%%V!"=="!ITEM!" set "FLAG="
)
rem // Check flag:
if defined FLAG (
rem // Flag is empty, so current grandparent needs to be stored:
set "$ARRAY[!INDEX!]=!ITEM!"
rem // Transfer stored grandparent over localisation barrier:
for /F "delims=" %%E in ("$ARRAY[!INDEX!]=!ITEM!") do (
endlocal
set "%%E"
)
rem // Increment index
set /A "INDEX+=1"
) else endlocal
)
)
rem // Retrieving final count of grandparent directories:
set /A "INDEX-=1"
rem // Return stored grandparent paths:
set $ARRAY[
endlocal
exit /B
This should return D:\users\path\programs\otherstorage and D:\users\path\programs\storage in your situation, which are stored in the variables $ARRAY[1] and $ARRAY[2], respectively. Due to the array-style variables, this approach is flexible enough to cover also cases where more than two grandparent directories are present.
Based on your above sample this batch
#Echo off
Set Search=\\data\\userinfo.txt
pushd "D:\Users\path\program
For /f "Delims=" %%A in (
'Dir /B/S/A-D userinfo.txt ^|findstr "%Search%$"'
) Do Call :Sub "%%~fA" "%%~dpA.."
Popd
Goto :Eof
:Sub FullName DrivePath
Echo Found %~nx1
Echo in %~dp1
Echo Granny %~nx2
Set "Granny=%~nx2"
Echo in %~dp2
Echo -------
Should give this output (only partially tested)
Found userinfo.txt
in D:\Users\path\program\storage\data\
Granny storage
in D:\Users\path\program\
-------
Found userinfo.txt
in D:\Users\path\program\storage2\data\
Granny storage2
in D:\Users\path\program\
-------
The backslash in Search has to be doubled as it is an escape char for findstr

Windows batch file to find duplicates size files in harddisk (no matter the name and the extension)

I'd like a batch file ( Windows CMD is the interpreter, a .bat ) to do this type of task:
1) Search through a folder and its subfolders (entire harddisk)
2) Find files with the same size (no matter filename and extension)
3) Display these files (or not)
Thank you for any kind of help! :)
EDIT:
With the following commands, I can know all sizes that the files have...
#echo off
set "filename=*.*"
for %%A in (%filename%) do echo.Size of "%%A" is %%~zA bytes
but now the big problem is that you need to compare the first, with the remainder and so on!
The Batch file below should solve your problem; however, be aware that in despite of its apparent simplicity, it is based on advanced concepts, like array management in Batch files.
#echo off
setlocal EnableDelayedExpansion
rem Group all file names by size
for /R %%a in (*.*) do (
set "size[%%~Za]=!size[%%~Za]!,%%~Fa"
)
rem Show groups that have more than one element
for /F "tokens=2,3* delims=[]=," %%a in ('set size[') do (
if "%%c" neq "" echo [%%a]: %%b,%%c
)
This program may take too much time if the number of files is large or if the starting folder have a long path.

Renaming a batch file and keeping part of the filename

I've seen plenty of posts on similar requests but I can't quite find one that suits what I am trying to do. It is fairly simple but I cannot seem to get it.
ren FILE??.txt FILE%Year%%Month%%Day%??.txt
copy FILE%Year%%Month%%Day%??.txt C:\Users\me\Desktop\Batch\renamed\achod%Year%%Month%%Day%??.txt
I cannot get the script to keep the '??' which represents random characters the first file may have.
Any help is appreciated.
You won't be able to rename files directly using a wildcard character. Instead you need to locate all the applicable files and then rename each.
The script below works under the assumptions of your question/comments:
File name is 6 chars long.
Only the last 2 chars are interchangeable.
Of course, the script could be very easily adapted to accomodate other settings but this does just as you requested.
SETLOCAL EnableDelayedExpansion
REM Set your Year, Month, Day variable values here.
REM They will be used for file renaming.
...
CD "C:\Path\To\Files"
FOR /F "usebackq tokens=* delims=" %%A IN (`DIR "File??.txt" /B /A:-D`) DO (
REM Extract the last 2 chars of the file name.
SET FileName=%%~nA
SET First4=!FileName:~0,4!
SET Last2=!FileName:~-2!
REM Rename the file, inserting the new data.
RENAME "%%A" "!First4!%Year%%Month%%Day%!Last2!%%~xA"
)
ENDLOCAL

For loop in batch file reading a file of File Paths

I want to write a Windows batch file script that will loop through a text file of FILE PATHS, do some work using data from each file path, then ultimately delete the file.
I started by running the FORFILES command and sending its output (the #PATH parameter is the full path of any file it matches) to a text file (results.txt).
I end up with a results.txt file like this:
"C:/Windows/Dir1/fileA.log"
"C:/Windows/Dir1/fileA.log"
"C:/Windows/Dir2/fileC.log"
"C:/Windows/Dir3/fileB.log"
What I want to do is:
Use a FOR loop and read each line in the results.txt file
For each line (file path), strip out the directory name that the log file is sitting in (ie: Dir1, Dir2, etc..) and create a directory with that SAME name in a different location (ie. D:/Archive/Backups/Dir1, D:/Archive/Backups/Dir2, etc..) -- assuming the directory doesn't exist.
Move the actual .log file to a zip file in that directory [I have code to do this].
Delete the .log file from its original location. [Pretty straightforward]
I'm having trouble figuring out the best way to accomplish the first 2 steps. My FOR loop seems to stop after reading the very first line:
FOR /F "tokens=1,2,3,4,5,6,7,8,9,10 delims=\" %%G in ("results.txt") DO (
...
)
You don't want to parse the path with the tokens/delims options because you don't know how many directory levels you are dealing with. You want to preserve each line in its entirety. TO parse the path you want to use the FOR variable modifiers. (type HELP FOR from the command line and look at the last section of the output)
%%~pG gives the path (without the drive or file name). If we then strip off the last \, we can go through another FOR iteration and get the name (and possible extension) of the file's directory by using %%~nxA.
The toggling of delayed expansion is just to protect against a possible ! in the path. If you know that no path contains ! then you can simply enable delayed expansion at the top of the script and be done with it.
EDIT - this code has been modified significantly since Aacini pointed out that I misread the requirements. It should satisfy the requirements now.
for /f "usebackq delims=" %%G in ("results.txt") do (
set "myPath=%~pG"
setlocal enableDelayedExpansion
for /f "eol=: delims=" %%A in ("!myPath:~0,-1!") do (
endlocal
if not exist d:\Archive\Backups\%%~nxA md d:\Archive\Backups\%%~nxA
rem ::zip %%G into zip file in the D: location
rem ::you should be able to create the zip with the move option
rem ::so you don't have to del the file
)
)
I wrote this to timestamp files before offloading to SFTP.
Hope you find it useful.
The timestamp coding may seem irrelevant to your issue, but I left it because it's a good example of dissecting the filename itself.
I suggest you put an ECHO in front of the REN command for testing. Different shells may have different results.
In the end, the delayedexpansion command wasn't necessary. It was the sub-routine that fixed my issues with variables inside the loop. That could possibly be because of my OS ver. (Win 8.1) - It wouldn't hurt to leave it.
#echo off
cls
setlocal enabledelayedexpansion
if %time:~0,2% geq 10 set TIMESTAMP=%date:~10,4%%date:~4,2%%date:~7,2%_%time:~0,2%%time:~3,2%%time:~6,2%
if %time:~0,2% leq 9 set TIMESTAMP=%date:~10,4%%date:~4,2%%date:~7,2%_0%time:~1,1%%time:~3,2%%time:~6,2%
echo TimeStamp=%TIMESTAMP%
echo.
for %%G in (*.txt) do (
set OLDNAME=%%G
call :MXYZPTLK
)
dir *.txt
goto :EOF
:MXYZPTLK
echo OldName=%OLDNAME%
ren %OLDNAME% %OLDNAME:~0,-4%_%TIMESTAMP%%OLDNAME:~-4,4%
echo.
:END
You have two minor problems:
The path separator in the file is '/' but you use '\' in the for loop.
The quotes around "results.txt" stop it working.
This works. Don't write quotes to results.txt and you won't get a quote at the end of the filename.
#echo off
FOR /F "tokens=3,4 delims=/" %%I in (results.txt) DO (
REM Directory
echo %%I
REM File
echo %%J
)

Resources