Change file extensions and name at once using batch file - batch-file

I have a directory full of files output from some software as below:
Pending.001
Pending.002
Pending.003
etc.
I would like to use a .bat to rename them as below:
Pending001.csv
Pending002.csv
Pending003.csv
etc.
Any suggestions?

You could rename your files in a loop, using the modifiers ~n and ~x on the loop variable to extract, respectively, the name and the extension of each file to concatenate them in the new name. As the extension would be extracted together with the leading ., you would also need a variable to temporarily store the extension before removing the ..
Here's an example of how the above could be implemented:
#ECHO OFF
FOR %%I IN (Pending.*) DO (
SET "ext=%%~xI"
SETLOCAL EnableDelayedExpansion
RENAME "%%I" "%%~nI!ext:~1!.csv"
ENDLOCAL
)
The delayed expansion is enabled because it is needed for the evaluation of ext. "Normal" expansion, which uses %, wouldn't work here because that way the variable would be expanded just once before the loop started, when the variable was still empty. In contrast, with the delayed expansion, the variable will be expanded every time the particular command containing it is executed, i.e. on every iteration of the loop too.
The above batch file would process files in the current directory, i.e. you would first need to change to the directory where the files need to be processed, then call the batch. Or, if you would be doing that in e.g. Windows Explorer, you would likely need to copy that batch to the directory with the files to process and call it from there.
Alternatively, though, you might want to call it with a parameter specifying the directory to process. To use the parameter, change the script like this:
#ECHO OFF
FOR %%I IN ("%~1\Pending.*") DO (
SET "ext=%%~xI"
SETLOCAL EnableDelayedExpansion
RENAME "%%I" "%%~nI!ext:~1!.csv"
ENDLOCAL
)
Of course, you could also just specify a fixed path to the files if they are always in the same directory:
#ECHO OFF
FOR %%I IN ("D:\path\to\files\Pending.*") DO (
SET "ext=%%~xI"
SETLOCAL EnableDelayedExpansion
RENAME "%%I" "%%~nI!ext:~1!.csv"
ENDLOCAL
)
That way you would be able to call the batch from wherever you stored it.

Why don't you refer to these posts:
How can I mass rename files from the command line or using a 3rd party tool?
Bulk Rename Command
Bulk Rename Utility
Using batch files

Related

Windows bat file to rename multiple files with custom names

I have multiple folders with multiple files in each of them. The file names are as following: static-string-1.wav static-string-2.wav .... static-string-10.wav ... static-string-99.wav static-string-100.wav ... and so on. The static string remains same but the number before .wav changes from 1 - 999. Now the problem is if I want to play this on any media player or want to join all the files, windows will sort like 1,10,100,2,20,200 not 1,2,3,4,5,6,7,8,9,10 which messes up the playback. So to fix this, I have to rename each file from static-string-1.wav to static-string-0001.wav and so on.
Currently, I am doing a dir command to an output file and then I copy the file list in excel from where I do some playing around with concatenate and text to columns and come up with two columns of old name and new name which I then again convert to a .bat file with and run it. The bat file has multiple rows with individual rename commands something like this:
#echo off
rename <oldname1> <newname0001>
rename <oldname2> <newname0002>
.
..
exit
It is getting the work done but I know there is and easier and more efficient way to use for loops. I saw few example and previous answers but they dont have a similar requirement as me. Any help will be appreciated.
Leading zeros can be added and (later) truncated if not needed. This question is a possible duplicate, but I don't like how files are sorted like that either.
Delaying the expansion allows b to change within the for loop instead of being static (haha puns) throughout the whole program. Therefore you can increment b each loop and rename the files. This is a simple example:
#echo off
setlocal ENABLEDELAYEDEXPANSION
for /l %%a in (1,1,99) do (
set b=00%%a
rename static-string-%%a.wav static-string-!b:~-2!.wav
)
This should work. Contact me if you need more help
Below is a significant improvement to Clayton's answer
Only numbers less than 100 need be modified
The script automatically works with any static prefix. See How does the Windows RENAME command interpret wildcards? for an explanation of how this works.
The script reports any file names that could not be renamed
#echo off
setlocal enableDelayedExpansion
for /l %%N in (1 1 99) do (
set "n=00%%N"
for %%F in (*-%%N.wav) do ren "%%F" *-!n:~-3!.* || >&2 echo ERROR: Unable to rename "%%F"
)
Or, you could grab my JREPL.BAT regular expression renaming utility and simply use:
jren "-(\d{1,2})(?=\.wav$)" "'-'+lpad($1,'000')" /j

Incremental rename batch output before overwritten

I am using a basic command line program to convert .xml files to .csv. I have it performing the action on all .xml files in the directory using
FOR %I in (..\xmlinput\*.xml) DO XmlToCsv.Console.exe -xml %I -dir ..\csvoutput\
This works fine but all output files are named r.csv, so each time it loops the previous r.csv is overwritten. How can I incrementally rename the output (r1.csv, r2.csv, etc.) before the next loop runs and overwrites the previous output? (the XmlToCsv.Console.exe has no additional parameters to use). Many thanks.
In a batch-file, you can try this:
#echo off
setlocal EnableDelayedExpansion
set "counter=1"
FOR %%I in (..\xmlinput\*.xml) DO (
XmlToCsv.Console.exe -xml %%I -dir ..\csvoutput\
ren "..\csvoutput\r.csv" "r!counter!.csv"
set /a "counter+=1"
)
Note that because this is a batch-file and not a single command you need to use %%I instead of %I. You might also need to change the paths for the rename command.

Use Dos commands to copy file and preserve date in file name

I'm having trouble trying to copy and rename a file using only dos commands. I have a file of the format myfile20130218 and want to copy and rename it to some_other_file_20130218.
I know I can use copy source dest but I'm having trouble with how to isolate the date and preserve it. I cannot guarantee that he date will be today's date so that is ruled out, the source file will always be the same name.
I can run either a series of commands or a batch script, but thing that that I am currently having trouble with, is after I find a match that I need to copy, using myfile????????, how can I now get those file names to pull the dates off them?
EDIT: for clarification I will be looking at files in a known directory, as above, I will know the format of the file name, and will only be checking a specific directory for it. The process that checks the directory is a ConnectDirect file watcher, so when a file is found matching myfile20130218 I can fire off some commands, but don't know how to check the directory and get the name of the file present.
Something like this should work:
%oldname:~-8% extracts the last 8 characters from %oldname% which are then appended to the new filename.
Update: If you can identify the file with an external program and then call the batch script with the file name
copyfile.cmd C:\path\to\myfile20130218
you could do something like this:
set oldname=%~nx1
set newname=%~dp1some_other_file_%oldname:~-8%
copy "%~f1" "%newname%"
Update 2: If you know folder and the format you could call the script with the folder
copyfile.cmd C:\folder
and do something like this:
#echo off
setlocal EnableDelayedExpansion
for /f %%f in (
'dir /b "%~f1" ^| findstr /r "myfile[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]$"'
) do (
set oldname=%~f1\%%f
set newname=%~f1\my_other_name_!oldname:~-8!
copy "!oldname!" "!newname!"
)
endlocal
Edit: Script breakdown.
setlocal EnableDelayedExpansion enables variable expansion inside loops and conditionals.
for /f %%f in ('...') executes the command between the single quotes and then loops over the output of that command.
dir /b "%~f1" lists the content of the given directory (%~f1 expands to the full path of the first argument passed to the script) in simple mode (no header, no summary).
findstr /r "myfile[0-9]...[0-9]$" filters the input for strings that end with the substring "myfile" followed by 8 digits. The circumflex before the pipe (^|) escapes the pipe, because otherwise it would take precedence over the for command, which would effectively split the for command in half, resulting in an invalid command-line.
set oldname=%~f1\%%f assign the full path to a matching file to the variable oldname.
set newname=%~f1\my_other_name_!oldname:~-8! assign the full path to the new filename ("my_other_name_" followed by the trailing 8 digits from oldname) to the variable newname.
copy "!oldname!" "!newname!" I don't need to explain this, do I?

Find called batch files recursively

Our current build system is a mass of batch files that are called from a "master" batch file. How can I find all the batch files that are called from the "master" batch file?
Unfortunately the repository also contains a lot of other batch files and I cannot just list all the batch files in the working copy.
I don't care much about "simulating" a real execution. It would be fine if I got a list of all the files potentially called from the master batch file.
There are 299 files, so I'd rather not add an echo "" to each one of them.
Referenced batch files are called using "call xyz.bat", i.e. relative paths. Sometimes the batch files change the current working directory though, like so:
cd client
call mk.bat
cd ..
or
pushd client\install
call prepare.bat
popd
EDIT: added examples
Yuck - this problem is nearly impossible to solve perfectly for several reasons:
1) Batch files may be "called" by multiple mechanisms:
CALL scriptName
pipes, for example: scriptName | findstr ...
FOR /F, for example: for /f ... in ('scriptName ...') do ...
CMD, for example: cmd /c scriptName ... or %comspec% /c scriptName
START, for example start "" scriptName ...
2) Any of the above constructs may be present without being a "call" to a batch script. For example, CALL can be used to call a label, or to execute a command. FOR /F could be used to parse a string. etc.
3) No matter which "call" mechanism is used, the "call" target may be represented by a variable instead of the string literal. For example:
set "myScript=ScriptName"
REM the SET may not be anywhere near the CALL
call %myScript%
4) The script name and path might not appear in the code. It could be dynamically read from the file system, or derived via logic.
5) The actual call itself may be embedded as a value in a variable. This is true for any call mechanism. For example:
set "myCommand=CALL"
%myCommand% ScriptName
6) As you have noted in your question, the path to the script may be relative, and the script may change the current directory. Or the "call" may be relying on the PATH environment variable.
7) Any "called" script could itself "call" another script.
You could use FINDSTR to look for any of the call mechanisms, and you will likely find most of the "calls". But there will likely be many false positives. You could add the /N switch to prefix each matching line with the line number. Then you would need to check each matching line manually in your text editor to see if it is a "call" that you are interested in.
findstr /nir /c:"\<call\>" /c:"|" /c:"for */f " /c:"\<cmd\>" /c:"\<%comspec%\>" /c:"\<start\>" *.bat
There may be so many false positives that you might be better off manually tracing the logic of the entire script :-( This is especially true since there is no guarantee that FINDSTR will find all "calls", since the call itself could be masked behind a variable.
You could prepend logging to each of the batch files. I know you said you'd rather not, but it's not as hard as you might think.
#echo off
:: addlogging.bat patch|unpatch
::
:: addlogging.bat patch
:: finds every .bat file in the current directory and every subdir
:: beyond, and patches each, prepending an echo to a log file
::
:: addlogging.bat unpatch
:: finds every .bat file in the current directory and every subdir
:: beyond, and removes the logging patch
setlocal
if #%1==#patch goto next
if #%1==#unpatch goto next
goto usage
:next
for /f "delims=" %%I in ('dir /s /b *.bat') do (
if not "%%I"=="%~f0" (
if #%1==#patch (
set /p I="Patching %%I... "<NUL
echo #echo %%time%% %%~f0 %%* ^>^> "%%userprofile%%\Desktop\%%date:/=-%%.log">"%%~dpnI.tmp"
) else (
set /p I="Unpatching %%I... "<NUL
)
findstr /v "^#echo.*time.*~f0.*>>" "%%I">>"%%~dpnI.tmp"
move /y "%%~dpnI.tmp" "%%I">NUL
echo Done.
)
)
goto :EOF
:usage
echo usage: %~nx0 patch^|unpatch

Bat file find folder of file

So my many attempts to search for a solution have resulted in a million ways to find the folder of the bat file being executed, however what I am looking to do is find the folder for the filename being passed to the bat file.
Example:
C:\Temp\runthis.bat "C:\Blah\Ah Argh\rage.txt"
I want to get a string within that bat file that is simply "C:\Blah\Ah Argh\", alternatively I would also be able to work with getting a string of "rage.txt"
Editing to explain why: Looking to check for the filename within another txt file which is the directory listing of a ftp server to verify that a file successfully uploaded to it. Then if successful I need to move the file to a subfolder of the original folder \uploaded\ but we have many of these folders setup so I can't hard code it.
Thanks
#echo off
The file path is %~dp1
The file name is %~nx1
The parameter modifiers are the same as for FOR variables.
Type 'HELP CALL' from a command prompt for a full list of parameter modifiers.
#echo off
if %1X==X echo Syntax: %0 "path"
rem The for loop doesn't actually loop. You can split strings with it, but in
rem this case we don't. So there is only one iteration in which %%X will
rem contain the full path.
rem Pass it %1, which is the first parameter. Note the quotes, which are
rem required if you don't add quotes around the parameter and optional (but
rem still valid) when you do.
for /F "delims=|" %%X in ("%1") do (
rem FOR LOOP variables can be used with certain modifiers, preceeded by a
rem tilde. In this case I'm using d and p, which stand for drive and path,
rem effectively trimming the file name from the path.
echo %%~dpX
rem The ~n modifier selects the file name only. ~x is for extension
echo %%~nxX
)

Resources