Combining two FOR commands (/f /l) in a batch file - for-loop

Okay guys, I am fairly new to working with batch files and I have two files I have previously created which are both working independantly.
I am looking to combine them but I still do not fully understand the FOR command. I was hoping someone could combine these two sets of code into one and if possible explain how the came up with the code they used from my two sources.
This file copies another file (in this case test.txt) to every subdirectory in a directory
FOR /R d:\ %%A IN (test.txt) DO copy d:\%username%\Desktop\Test\Resources\test.txt %%A
FOR /R h:\ %%A IN (test.txt) DO copy d:\%username%\Desktop\Test\Resources\test.txt %%A
This file copies and renames another file X amount of times (in this case 5) renaming each succesive copy in increments of 1.
For /l %%1 in (1,1,10) do (
copy test.txt test%%1.txt > nul
)
Basically I want the selected file (test.txt) to be copied from a set location to every subdirectory within a directory and then copied in each folder X amount of times and renamed with increasing values e.g.
test1.txt
test2.txt
test3.txt
etc.
Thankyou in advance.

This is actually very straight forward.
You already have functioning code that copies from the source to each subdirectory. In pseudo code: FOR (each directory) DO COPY source to target.
You also have code that can copy the file 10 times with incrementing names. You want to do this for each directory in the 1st step. So, again in pseudo code, it will look something like this:
FOR (each directory) DO (
COPY source to target
FOR (N=1 TO 10) DO COPY source to targetN
)
None of the syntax above is real, except that the parentheses after the DO are actually how you allow a batch FOR command to execute a block of multiple commands. (Actually there are other techniques to do this, but the parens work just fine.)
The part that you are missing is how to insert the incrementing number into the %%A target name. This is done by using FOR variable modifiers, as described at the end of the FOR documentation that you can access from the command line by typing HELP FOR, or FOR /?.
The modifiers allow you to deconstruct a file specification into its component parts. Note that the file doesn't have to physically exist, the file spec can still be broken down into the constituent parts.
%%~dpnA = drive:\path\baseName (no extension)
%%~xA = .extension, including the dot.
You've already got the incrementing number - I'm going to use %%N instead of %%1. So the full target will be the concatenation of the 3 components: %%~dpnA%%N%%~xA.
Putting it all together gives the full solution:
FOR /R d:\ %%A IN (test.txt) DO (
copy d:\%username%\Desktop\Test\Resources\test.txt %%A
FOR /L %%N IN (1 1 10) DO copy d:\%username%\Desktop\Test\Resources\test.txt %%~dpnA%%N%%~xA
)

Related

Windows Batch rename, renames a file 2 times [duplicate]

The command for can be used to enumerate a directory and apply (a) certain command(s) on each item. With the /R the same can be accomplished for a full directory tree.
What happens when the content of the enumerated directory (tree) is changed by the command(s) in the body of the for command?
Supposed we have the directory D:\data with the following content:
file1.txt
file2.txt
file3.txt
The output of for %F in ("*.txt") do echo %F when executed in said directory will reflect the above list obviously.
However, what is the output of the for loop when a command in the for body modifies the content of the directory? For instance, one of the files in the list is deleted, let's say file3.txt, before it is actually iterated? Or if a new file is created, like file4.txt, before completion of the loop?
How does for /R behave in that context? Supposed there are several sub-directories sub1, sub2, sub3, each containing the above list of files; for /R is currently iterating through sub2, sub1 has already been processed, but sub3 not yet; the contents of sub1 and sub3 are changed at that point (when currently walking through sub2 as mentioned); what will be enumerated then? I guess, the change of the content of sub1 won't be recognised, but what about sub3?
Finally, is there a difference in behaviour of for or for /R when being executed in the command prompt or from a batch file? And are there differences in different Windows versions?
Note:
See also my similar question about the forfiles command.
This is an excellent question!
Let's concentrate on plain for command for a moment. There is a known bug related to this command when it is used to rename files. For example:
set i=0
for %%a in (*.txt) do (
set /A i+=1
ren "%%a" !i!.txt
)
In this case is frequently that certain files be renamed twice, and even three times in certain cases. The problem is that this behavior depends on a series of factors that are not documented, like the position of the first renamed file inside the list of original files and several other points. Similarly, if a file is deleted before it is processed by the for, a "File not found" message is usually issued (although not ALL times). If a new file is created in the directory after the for started execution, then it may or may not be processed by the for depending (again) on a series of factors. The usual way to avoid the problem with rename is to force for command to first read the whole list of files and then process the list:
for /F "delims=" %%a in ('dir /B *.txt') do (
set /A i+=1
ren "%%a" !i!.txt
)
This way, don't matters the changes that can be made on the files in the disk: the for /F command will always process the original file list.
A similar problem happen with for /R command, but in this case the possibility of problems is greater because there are more directories where dynamic changes can be made. Again: the exact behavior depends on a series of unknown factors and the way to avoid them is via for /F ... in ('dir /S /B'). However, if you are really interested in this point, I encourage you to made a series of tests on the subject (and post the results). ;)

Parsing every file in submap recursively to output folder whilst maintaining relativity

I'm using the following batch code to convert all files in a certain directory if the target file doesn't already exist however I'm stuck at getting this to run through every submap and file within that (and keep the output relative with that submap)
So I currently use this:
for %%f in (input/textures/*.*) do ( IF NOT EXIST "ouput/textures/%%~nf.dds" (
"bin/ThempImageParser.exe" "input/textures/%%f" "ouput/textures/%%~nf.dds"
)
)
This works perfectly for a single folder (as was intended), it takes all the files in that specific folder, and passes them as arguments to my executable, which then outputs the file on the path of the second argument.
However this also contains a flaw (this is an additional problem though..) as it does not work if the output -folder- does not exist, so if possible I'd also want it to create the folder if need be.
I've found some batch documentation (I really don't have much experience with Batch) showing me a command called FORFILES and the /R parameter, however I couldn't adjust this so it'd keep the relative paths for the output too, it'd require string manipulation and I have no clue on how to do that.
So the result I'm after is something like this, it takes any file deeper than "input/textures/ for example:
input/textures/some/very/deep/submap/why/does/it/go/on/myfile.anyExtension
it should then take that file (and relative path) and basically change "input" with "output" and replace the file extension with .dds like this:
ouput/textures/some/very/deep/submap/why/does/it/go/on/myfile.dds
and pass those two strings to my executable.
#ECHO Off
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir\t w o"
SET "destdir=U:\destdir\wherever\something"
FOR /f "delims=" %%a IN ('xcopy /y /L /s "%sourcedir%\*"') DO (
SET "destfile=%%a"
SET "destfile=!destfile:*%sourcedir%=%destdir%!"
IF /i "%%a" neq "!destfile!" (
FOR %%m IN ("!destfile!") DO IF NOT EXIST "%%~dpm%%~na.dds" (
ECHO MD "%%~dpm"
ECHO "bin\ThempImageParser.exe" "%%a" "%%~dpm%%~na.dds"
)
)
)
GOTO :EOF
You would need to change the settings of sourcedir and destdir to suit your circumstances.
First, perform an xcopy with the /L option to list-only the individual fullnames of files that would be copied by the xcopy.
Assign each name found from %%a to destfile, then remove all characters before the source-directoryname from that filename, and replace that string with the destination directoryname.
This will yield the destination name for the file (with the original extension). The only exception will be the very last output line, which is a count-of-files report. Since this line will not contain the source directoryname, the replacement will not take place, so %%a will be the same as !destfile! - so we eliminate that.
Now assign the destination filename to a metavariable so we can select its various parts, and if the filename made from the destination drive and pathname, the name part of the original file and .dds does not exist, then make the destination directoryname and execute the imageparser, providing the desired output filename.
Note that these last two are ECHOed instead of being executed for testing purposes. Remove the ECHOes to actually perform the command.
Note that / is a switch-indicator, \ is a directory-separator.
Note that MD will report an error if the directory already exists. Append 2>nul to the end of the md command to suppress that error message.

Batch: Save directory listing into list-variable

I want to take a file from a source directory (C:\Users\Desktop\Source\Test.xlsx) and want to replicate it to several folders, all having the same structure. So I have to change just one folder name. All done via one batch file and just one log-file which is created afterwards.
To have dynamically expansion of my batch-procedure here, I want to do a filtering of the elements of the root folder (C:\Users\Desktop\Replica\), which shows me the childs (one two three four five) where the file needs to be copied at inside a testfolder. There I have done the following at the moment, which is static:
set "list=one two three four five"
(
for %%i in (%list%) do (
xcopy "C:\Users\Desktop\Source\Test.xlsx" "C:\Users\Desktop\Replica\%%i\testfolder" /Y
echo(%%i
echo(
)
)>copylog.txt
So, my question is basically who I do the listing of just the folders under "replica" and put those inside a list variable I can use in the code as already written. I only found sources where these are just saved within a separate .txt-file, but I just want to keep it inside the batch.
Any guesses on this one?
By the way: I experienced by saving a path just as a variable, I couldn't execute xcopy %source% %target% (even when having "%source%" or source = "Path"). What is the problem here?
You don't need to search the directories, put them into a variable and then iterate over the values in the variable. Just iterate over the directories
...
for /d %%i in ("C:\Users\Desktop\Replica\*") do (
xcopy /y "C:\Users\Desktop\Source\Test.xlsx" "%%~fi\testfolder"
...
The for /d will iterate over the indicated set of directories. In the xcopy command %%~fi is used to retrieve the full path of the directory for the current iteration.

At which point does `for` or `for /R` enumerate the directory (tree)?

The command for can be used to enumerate a directory and apply (a) certain command(s) on each item. With the /R the same can be accomplished for a full directory tree.
What happens when the content of the enumerated directory (tree) is changed by the command(s) in the body of the for command?
Supposed we have the directory D:\data with the following content:
file1.txt
file2.txt
file3.txt
The output of for %F in ("*.txt") do echo %F when executed in said directory will reflect the above list obviously.
However, what is the output of the for loop when a command in the for body modifies the content of the directory? For instance, one of the files in the list is deleted, let's say file3.txt, before it is actually iterated? Or if a new file is created, like file4.txt, before completion of the loop?
How does for /R behave in that context? Supposed there are several sub-directories sub1, sub2, sub3, each containing the above list of files; for /R is currently iterating through sub2, sub1 has already been processed, but sub3 not yet; the contents of sub1 and sub3 are changed at that point (when currently walking through sub2 as mentioned); what will be enumerated then? I guess, the change of the content of sub1 won't be recognised, but what about sub3?
Finally, is there a difference in behaviour of for or for /R when being executed in the command prompt or from a batch file? And are there differences in different Windows versions?
Note:
See also my similar question about the forfiles command.
This is an excellent question!
Let's concentrate on plain for command for a moment. There is a known bug related to this command when it is used to rename files. For example:
set i=0
for %%a in (*.txt) do (
set /A i+=1
ren "%%a" !i!.txt
)
In this case is frequently that certain files be renamed twice, and even three times in certain cases. The problem is that this behavior depends on a series of factors that are not documented, like the position of the first renamed file inside the list of original files and several other points. Similarly, if a file is deleted before it is processed by the for, a "File not found" message is usually issued (although not ALL times). If a new file is created in the directory after the for started execution, then it may or may not be processed by the for depending (again) on a series of factors. The usual way to avoid the problem with rename is to force for command to first read the whole list of files and then process the list:
for /F "delims=" %%a in ('dir /B *.txt') do (
set /A i+=1
ren "%%a" !i!.txt
)
This way, don't matters the changes that can be made on the files in the disk: the for /F command will always process the original file list.
A similar problem happen with for /R command, but in this case the possibility of problems is greater because there are more directories where dynamic changes can be made. Again: the exact behavior depends on a series of unknown factors and the way to avoid them is via for /F ... in ('dir /S /B'). However, if you are really interested in this point, I encourage you to made a series of tests on the subject (and post the results). ;)

How to rename and add incrementing number suffix on multiple files in Batch Script?

I have 500 files coming in and I need to first check if any file(s) exist then rename all of them regardless of what their filename is (the files are named in a different language).
No need to process them in any order.
Rename:
1. “¦X¼d¬f-20110703-¦+¦dñHÑ-ª-¦=¬¦.xls”
2. “¦X¼d¬f-20110707-¦+¡¦-+¡8.xls”
3. “¦X¼d¬f-20110707-¦+¡¦ñj¦«.xls”
4. “¦X¼d¬f-20110708-¦+¡¦¬M¼n.xls”
5. “¦X¼d¬f-20110713-¦d¼O¼n¦hÑP.xls”
.
.
.
500
To:
“TWN_CH_INV_VISIT_FORM_01.xls”
“TWN_CH_INV_VISIT_FORM_02.xls”
“TWN_CH_INV_VISIT_FORM_03.xls”
“TWN_CH_INV_VISIT_FORM_04.xls”
“TWN_CH_INV_VISIT_FORM_05.xls”
.
.
.
“TWN_CH_INV_VISIT_FORM_500.xls”
Hope you could help me on this one. I’ve been trying to do this for weeks.
a simple FOR with a count (SET /A) should do what you need.
setlocal enabledelayedexpansion
SET /A COUNT=0
FOR %%A IN (*.xls) DO (
SET /A COUNT+=1
REN "%%A" "TWN_CH_INV_VIST_FORM_!COUNT!.xls"
)
See HELP FOR and HELP SET
This is a deceptively difficult question to solve.
The 5 year old PA answer has a few problems.
1) The FOR loop begins iterating without buffering the entire directory tree, so it has the potential to rename a file that has already been renamed. I believe that is why the 7 file is missing within r0mmel's comment.
2) Delayed expansion occurs after for variables are expanded, so the file name will be corrupted and the rename will fail if the name contains a ! character.
3) A rename can fail if there already exists a TWN_CH_INV_VIST_FORM_n.xls file with the same number.
At first I thought I could solve the problem using the following:
#echo off
for /f "delims=: tokens=1*" %%A in (
'dir /b *.xls ^| findstr /n "^"'
) do ren "%%B" "TWN_CH_INV_VIST_FORM_%%A.xls.new"
ren *.txt.new *.
I use DIR /B to list the files, and pipe that result to FINDSTR to prefix each file name with a line number, followed by a colon.
I then use FOR /F to iterate and parse the results into the number and the file name. FOR /F buffers the entire result before iterating, so I don't need to worry about renaming the same file twice.
I first give the renamed files a .xls.new "extension", just in case your directory already has files that meet the TWN_CH_INV_VIST_FORM_n.xls pattern. You don't want any name collisions. The final REN command then simply removes the .new extension to leave the desired .xls.
BUT, I just noticed that the original file names have lots of weird characters that could involve unicode that is not in the current code page. FOR /F does not play well with unicode.
There is one other minor issue in that the above does not pad the number to a fixed width. (this could have been solved easily enough)
So at this point it is time to break out my JREN.BAT regular expression renaming utility. It is pure script (hybrid batch / JScript) that runs natively on any Windows machine from XP onward. It has a built in facility to incorporate a fixed width incrementing number in the new name, and it works fine with unicode. I still temporarily give the new name the ".xls.new" extension to avoid any name collisions.
#echo off
call jren "^.*" "'TWN_CH_INV_VIST_FORM_'+$n+'.xls.new'" /j /npad 3 /fm *.xls
ren *.xls.new *.
I chose to pad the incrementing number to 3 digits instead of 2 because the OP said there could be 500 files.
Full documentation for JREN.BAT is available from the command line via jren /?, or jren /?? if you want paged output.

Resources