Creating input subfolder structure inside output folder - windows

I have a batch script that:
read input files from a folder
elaborate them
store output files in another folder
Example code:
set pathTmp=D:\a\b\c
set pathIn=%pathTmp%\in
set pathOut=%pathTmp%\out
for /f %%i in ('dir /b %pathIn%') do (
java XXX.jar %pathIn%\%%i >> %pathOut%\%%i
)
Now I'd like to modify it to read files from all subfolders of pathIn and put the output file in the same subfolder but under pathOut.
Example: if input file is in pathIn\zzz, the output file must be in pathOut\zzz.
How can I recreate the input subfolder structure inside output folder?

I would use xcopy together with the /L switch (to list files that would be copied) to retrieve the relative paths. For this to work, you need to change to the directory %pathIn% first and specify a relative source path (for this purpose, the commands pushd and popd can be used).
For example, when the current working directory is D:\a\b\c\in and its content is...:
D:\a\b\c\in
| data.bin
+---subdir1
| sample.txt
| sample.xml
\---subdir2
anything.txt
...the command line xcopy /L /I /S /E "." "D:\a\b\c\out" would return:
.\data.bin
.\subdir1\sample.txt
.\subdir1\sample.xml
.\subdir2\anything.txt
3 File(s)
As you can see there are paths relative to the current directory. To get rid of the summary line 3 File(s), the find ".\" command line is used to return only those lines containing .\.
So here is the modified script:
set "pathTmp=D:\a\b\c"
set "pathIn=%pathTmp%\in"
set "pathOut=%pathTmp%\out"
pushd "%pathIn%"
for /F "delims=" %%I in ('xcopy /L /I /S /E "." "%pathOut%" ^| find ".\"') do (
md "%pathOut%\%%I\.." > nul 2>&1
java "XXX.jar" "%%I" > "%pathOut%\%%I"
)
popd
Additionally, I placed md "%pathOut%\%%I\.." > nul 2>&1 before the java command line so that the directory is created in advance, not sure if this is needed though. The redirection > nul 2>&1 avoids any output, including error messages, to be displayed.
I put quotation marks around all paths in order to avoid trouble with white-spaces or any special characters in them. I also quoted the assignment expressions in the set command lines.
You need to specify the option string "delims=" in the for /F command line, because the default options tokens=1 and delims=TABSPACE would split your paths unintentionally at the first white-space.
Note that the redirection operator >> means to append to a file if it already exists. To overwrite, use the > operator (which I used).

You could do something like this:
#setlocal EnableDelayedExpansion
#echo off
set pathTmp=D:\a\b\c
set pathIn=%pathTmp%\in
set pathOut=%pathTmp%\out
REM set inLength=ADD FUNCTION TO CALCULATE LENGTH OF PATHIN
for /f %%i in ('dir /b /s %pathIn%') do (
set var=%%i
java XXX.jar %%i >> %pathOut%\!var:~%inLength%!
)
This will strip the length of the pathIn directory from the absolute path leaving only the relative path. Then it appends the relative path onto the pathOut var
You would need to find or write a function to get the length of the the pathIn string. Check out some solutions here.

Related

Batch File Variable to copy specific files

I'm trying to copy specific files from C: to "X: for example". The files are named with the same format.
A1234_ZZabc123_DT1_F1.tst
A4567_ZZdef4567_DT2_F2.tst
A8901_ZZghi1289_DT1.tst
A2345_ZZjfkbu12_to_modify.tst
A6789_ZZlmny568_F1_to_modify.tst
A1234_ZZabc478_DT1.txt
I want to copy only the .tst files, and with the same format as the first 3 Axxxx_ZZyyyxxx_DTx_Fx.tst where x=number and y=letter.
After ZZ, it might be 4 letters and 3 numbers, or 5 letters and 4 numbers, like a "namecode".
Example: ZZkusha122 or ZZkus1551.
I need to copy the folders along with the files too.
I'm new to coding and really need some help.
I need to find and copy all those files along 10k+ files together
You claim that the first 3 of your example filenames fit the pattern you describe. I believe that only two do.
#ECHO OFF
SETLOCAL
rem The following setting for the directory is a name
rem that I use for testing and deliberately includes spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files"
FOR /f "delims=" %%e IN (
'dir /b /a-d "%sourcedir%\A*.tst"^|findstr /X /I /R "A[0-9][0-9][0-9][0-9]_ZZ[a-z][a-z][a-z]_DT[0-9]_F[0-9].tst" '
) DO ECHO COPY "%sourcedir%\%%e" X:
)
GOTO :EOF
Always verify against a test directory before applying to real data.
The required COPY commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO COPY to COPY to actually copy the files. Append >nul to suppress report messages (eg. 1 file copied)
Simply execute a dir command that reports only filenames matching the *.tst mask. Filter this list with findstr which /X exactly matches the regular expression provided. findstr only has a limited implementation of regular expressions. The /I forces a case-insensitive match. If you want case-sensitive, remove the /I and change each [a-z] to [a-zA-Z] (leave as-is if you want lower-case only in these positions.)
See findstr /? from the prompt for more documentation, or search for examples on SO.
---- revision to cater for multiple filemasks and subdirectories ---
#ECHO OFF
SETLOCAL
rem The following setting for the directory is a name
rem that I use for testing and deliberately includes spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files"
SET "maskfile=%sourcedir%\q74442552.txt"
FOR /f "tokens=1*delims=" %%e IN (
'dir /b/s /a-d "%sourcedir%\*.tst"^|findstr /E /I /R /g:"%maskfile%" '
) DO ECHO COPY "%%e" X:
)
GOTO :EOF
Changes:
Establish a file, name irrelevant, as maskfile
The dir command requires the /s switch to scan subdirectories
The filemask for the dir command loses the initial A
The findstr command replaces the /X switch with /E
The findstr command loses the regex expression. These are transferred to a file and the file is nominated by the /g: switch.
The copy command loses the source-directory as the directory will be included in %%e
The file "q74442552.txt" contains lines that are of the form
A[0-9][0-9][0-9][0-9]_ZZ[a-z][a-z][a-z]_DT[0-9]_F[0-9].tst
A[0-9][0-9][0-9][0-9]_ZZ[a-z][a-z][a-z]_to.*.tst
This time, %%e acquires the full pathname of the files found. Since the filemask ends .tst, the only filenames to pass the dir filter will be those that end .tst.
The /e switch tells findstr to match string that End with the regex strings in the file specified as /g:.
The strings in the file must comply with Microsoft's partial regex implementation, one to a line.
In summary, findstr uses as regex
Any character,literally
[set] any character of a set of characters
[^set] any character not in a set of characters
. any character
.* any number of any character
prefix any of the special characters with\ to use it literally
a set may include a range by using low-high
So - you then need to brew-your own using the examples I've supplied. The second line matches Axxxx_ZZyyy_to{anything}.tst for instance.
--- Minor revision to deal with maintaining destination-tree -----
(see notes to final revision for why this doesn't quite work)
#ECHO OFF
SETLOCAL
rem The following setting for the directory is a name
rem that I use for testing and deliberately includes spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files"
SET "maskfile=%sourcedir%\q74442552.txt"
SET "destdir=u:\your results"
FOR /f "tokens=1*delims=" %%e IN (
'dir /b/s /a-d "%sourcedir%\*.tst"^|findstr /E /I /R /g:"%maskfile%" '
) DO ECHO "%%~nxe"&XCOPY /Y /D /S "%sourcedir%\%%~nxe" "%destdir%\">nul
)
GOTO :EOF
This version adds the destination root directory as destdir.
The dir ... findstr... works as before to list the filenames to copy.
The prior version used echo copy to report the proposed copy operation, but the destination was always the same directory.
The replacement XCOPY line maintains the directory structure at the destination.
Note : the XCOPY is "live". The files will be copied to the destination if run as-is. Always verify against a test directory before applying to real data.
To "defuse" the XCOPY, add the /L switch and remove the >nul. This will cause XCOPY to report the source name that would be copied instead of copying it. (The >nul suppresses the report)
The /D only copies source files that eitherr do not exist in the destination of have a later datestamp in the source.
The action is to xcopy each filename found (%%~nxe) from the source directory tree to the destination. Therefore, any file xyz.tst found anywhere in the source tree will be xcopyd to the destination tree. The /D means that once xyz.tst is encountered on the source tree, it will be skipped should it be encountered again.
--- Final (I hope) revision ---
#ECHO OFF
SETLOCAL
rem The following setting for the directory is a name
rem that I use for testing and deliberately includes spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=U:\Users\tocil\Desktop\aoi"
SET "maskfile=%sourcedir%\q74442552.txt"
SET "destdir=u:\your results"
FOR /f "tokens=1*delims=" %%e IN (
'dir /b/s /a-d "%sourcedir%\*.tst"^|findstr /E /I /R /g:"%maskfile%" '
) DO (
rem drive and path to 'dirname' - has terminal "\"
SET "dirname=%%~dpe"
rem remove the sourcedir from dirname
FOR %%y IN ("%sourcedir%") DO CALL SET "dirname=%%dirname:%%~y=%%"
rem copy or xcopy the file to the destination.
FOR /f "tokens=2delims==" %%y IN ('set dirname') DO XCOPY /Y "%%e" "%destdir%%%y">nul
)
)
GOTO :EOF
Always verify against a test directory before applying to real data.
Note to self: Only if the filemask provided to XCOPY is ambiguous (ie. contains ? or *) will XCOPY obey the /s switch unless the target file exists in the starting source directory.
hence
xcopy /s sourcedir\myfile destdir
will copy myfile from the entire tree ONLY if sourcedir\myfile exists.
xcopy /s sourcedir\myf?le destdir
will copy myfile from the entire tree regardless. Unfortunately it will also copy myfale and myfule as well.
Hence, the new approach.
First, perform a dir /b /s to get all of the filenames and filter as before. This is being assigned to %%e.
Take the drive and path only of the file and assign to dirname.
The next step is a little complex. First, set the value of %%y to the name of the source directory. Next, use a parser trick to remove that name from dirname. The mechanics are: Parse the %%dirname:%%~y=%% (because the call causes the set to be executed in a sub-shell) whuch does the normal left-to-right evaluation. %% is an escaped-%, so is replaced by %; %%y is an active metavariable so is replaced by (the name of the source directory) and the ~ causes the quotes to be stripped from that name. The resultant command executed is thus SET "dirname=%dirname:nameofsourcedirectory=%"
So now we can construct a copy-class instruction. dirname now contains the relative directory for the destination, which we can extract from the environment by parsing a set listing (Could also be done with delayed expansion) where %%y gets set to the relative directory and has both a leading and trailing backslash, so the destination directory is simply "%destdir%%%y". XCOPY then knows to create that directory if necessary (%%y has a trailing backslash) and we know the source filename is in %%e.
You could also use a copy to do the same thing, but you'd need to create the destination directory first. Another advantage of XCOPY is that you can also specify the /d switch to not copy files that have an earlier date over files that have a later date.

How do I get a relative path out of a windows batch file using echo?

How do I get relative directories / partial paths to display as echo output from a windows .bat file?
How to split the filename from a full path in batch?
Talks about everything but.
I've found drive letters, filenames, extensions, shortened (8.3) names, and full paths - but no relative paths.
I'm running a recursive FOR /R loop; which traverses sub-directories. I would like something - that doesn't have twenty characters of useless path info - that tells me which directory each duplicate file lives in... without hardcoding the .bat file to live in a certain directory/path?
Maybe a solution would be to measure the length of the script's path and cut that off of the front of the full path? But I don't know how to manipulate that.
Script could be in many locations:
F:\a.bat<BR>
F:\Dir1\fileA.txt<BR>
F:\Dir20\fileA.txt
C:\Users\Longusername\Desktop\Container\a.bat<BR>
C:\Users\Longusername\Desktop\Container\Dir1\fileA<BR>
C:\Users\Longusername\Desktop\Container\Dir20\fileA
And right now the only options I have for output are (%%~nxG):
fileA.txt
fileA.txt
Which doesn't tell me which directory each file is in...or (%%~pnxG)
\Users\Longusername\Desktop\Container\Dir1\fileA.txt
\Users\Longusername\Desktop\Container\Dir20\fileA.txt
What I'd like, from any location:
\Dir1\fileA.txt
\Dir20\fileA.txt
Could be missing the leading \, but that's negligible. Other options than echo are permissible if they'll work on most window machines. They may lead to more questions, though - as I've figured out my other pieces with echo.
quite easy, if you think about it: just remove the current directory path (%cd%):
#echo off
setlocal enabledelayedexpansion
for /r %%a in (*.txt) do (
set "x=%%a"
echo with \: !x:%cd%\=\!
echo without \: !x:%cd%\=!
)
By the way: \folder\file always refers to the root of the drive (x:\folder\file), so it's not exactly a relative path.
This is similar to the already accepted answer, but with delayed expansion enabled only where needed. This should correctly output filenames containing ! characters.
#Echo Off
SetLocal DisableDelayedExpansion
Set "TopLevel=C:\Users\LongUserName"
For /R "%TopLevel%" %%A In ("*.txt") Do (
Set "_=%%A"
SetLocal EnableDelayedExpansion
Echo=!_:*%TopLevel%=!
EndLocal
)
Pause
You could also use Set "TopLevel=%~dp0", (the running script's directory) or Set "TopLevel=%~dp0..", (the running script's parent directory)
One potential benefit of the above method is that you can also use a location relative to the current directory for the value of %TopLevel% too, (in this case, based upon the initial example, the current directory would be C:\Users):
Set "TopLevel=LongUserName"
Although this would only work correctly if LongUserName didn't already exist as content of the path earlier in the tree.
You could use xcopy together with its /S (include sub-directories) and /L (list but do not copy) options since it returns relative paths then, so you do not have to do any string manipulation, which might sometimes be a bit dangerous, particularly when the current directory is the root of a drive:
xcopy /L /S /I /Y /R ".\*.txt" "\" | find ".\"
The appended find command constitutes a filter that removes the summary line # File(s) from the output.
To capture the output of the aforementioned command line just use a for /F loop:
for /F "delims=" %%I in ('
xcopy /L /S /I /Y /R ".\*.txt" "\" ^| find ".\"
') do (
rem // Do something with each item:
echo/%%I
)

Loop through folders in subdirectories and combine text files

I am wanting to loop through folders within a subdirectory and combine all text files into one file. I found some answers online but none seems to work. Any help is much appreciated. I have provided what I've found below. In the example below the DummyFolder has multiple subdirectories that contain .txt files that need to be merged into 1 file. I got code 3 to work yesterday but somehow I changed something and it is no longer working for some reason.
Code 1:
#echo off
set "header=C:\Users\user\Desktop\DummyFolder\Headings.txt"
set "folder=C:\Users\user\Desktop\DummyFolder\"
set "tempFile=%folder%\temp.txt"
for %%F in ("%folder%\*.txt") do (
type "%header%" >"%tempFile%"
type "%%F" >>"%tempFile%"
move /y "%tempFile%" "%%F" >nul
)
Also found this code (Code 2):
$startingDir = 'C:\Users\user\Desktop\DummyFolder\'
$combinedDir = 'C:\Users\user\Desktop\DummyFolder\CombinedTextFiles'
Get-ChildItem $startingDir -Recurse | Where-Object {
$txtfiles = Join-Path $_.FullName '*.txt'
$_.PSIsContainer -and (Test-Path $txtfiles)
} | ForEach-Object {
$merged = Join-Path $combinedDir ($_.Name + '_Merged.txt')
Get-Content $txtfiles | Set-Content $merged
}
Also found this code (Code 3):
#echo on
set folder="C:\Users\user\Desktop\DummyFolder\"
for /F %%a in ('dir /b /s %folder%') do (
if "%%~xa" == ".txt" (
(echo/------------------------------
type %%~a
echo/)>>"%~dp0list.txt"
)
)
In CMD you'd do something like this:
#echo off
set "basedir=C:\some\folder"
set "outfile=C:\path\to\output.txt"
(for /r "%basedir%" %f in (*.txt) do type "%~ff") > "%outfile%"
For use in batch files you need to change %f to %%f and %~ff to %%~ff.
In PowerShell you'd do something like this:
$basedir = 'C:\some\folder'
$outfile = 'C:\path\to\output.txt'
Get-ChildItem $basedir -Include *.txt -Recurse | Get-Content |
Set-Content $outfile
There are so many ways to do this. For example, using the Wolfram Language you can:
StringJoin ##
FileSystemMap[
If[FileExtension[#] == "txt", Import[#, "Text"]] &,
"C:\\Users\\user\\Desktop\\DummyFolder\\", Infinity, 1]
An then write the result using
Export[C:\\Users\\user\\Desktop\\, %, "Text"]
You can also do this with Python, Perl, etc.. use PowerShell only if you need to share your solution and want to avoid installers. I would not spend too much time learning 1981 technology (CMD).
Assuming that your source files are located in immediate sub-directories of the root directory DummyFolder and that you want the content of Headings.txt to occur once only on top of the resulting file, you could accomplish your task using the following script:
#echo off
rem // Define constants here:
set "folder=C:\Users\user\Desktop\DummyFolder"
set "header=%folder%\Headings.txt"
set "result=%folder%\merged.txt"
rem // Prepare result file, copy content of header file:
copy "%header%" "%result%" > nul
rem // Enumerate immediate sub-directories of the given root directory:
for /D %%D in ("%folder%\*") do (
rem // Enumerate matching files per sub-directory:
for %%F in ("%%~D\*.txt") do (
rem // Append content of current file to result file:
copy /Y "%result%" + "%%~F" "%result%" /B > nul
)
)
In case your source files are located anywhere in the directory tree DummyFolder, you need to make sure that the header file Headings.txt and the result file merged.txt are not iterated:
#echo off
rem // Define constants here:
set "folder=C:\Users\user\Desktop\DummyFolder"
set "header=Headings.txt"
set "result=merged.txt"
rem // Prepare result file, copy content of header file:
copy "%folder%\%header%" "%folder%\%result%" > nul
rem // Enumerate matching files in the whole given directory tree:
for /R "%folder%" %%F in ("*.txt") do (
rem // Exclude the header file to be re-processed:
if /I not "%%~nxF"=="%header%" (
rem // Exclude the result file to be processed:
if /I not "%%~nxF"=="%result%" (
rem // Append content of current file to result file:
copy /Y "%folder%\%result%" + "%%~F" "%folder%\%result%" /B > nul
)
)
)
This may be a simple answer for what you are looking for, the usebackq is important to allow "" around paths. tokens=* to include all information. To use in a console instead of a batch file change %% to %.
for /f "tokens=*" %%a in ('dir /s /b C:\testpath\*.txt') do (for /f "usebackq tokens=*" %%b in ("%%a") do (echo %%b >> C:\test.txt))
Code 3 is not bad but it won't work with spaces in a path because you use the standard delims as you're not providing one. Also there a several other errors about working with spaces in a path.
The following code works and combine all txt files in all subdirectories. It will create a new file list.txt in the folder where this batch file is located. If there is already an existing list.txt it will be overwritten. Note that it's a batch file:
#echo off
set "folder=C:\Users\user\Desktop\DummyFolder\"
rem create new empty file: list.txt in directory of batch file: %~dp0
break>"%~dp0list.txt"
rem loop through all output lines of the dir command, unset delimns
rem so that space will not separate
for /F "delims=" %%a in ('dir /b /s "%folder%"') do (
rem just look for txt files
if "%%~xa" == ".txt" (
rem don't use the list.txt
if not "%%a" == "%~dp0list.txt" (
rem append the output of the whole block into the file
(echo/------------------------------
type "%%a"
echo/)>>"%~dp0list.txt"
)
)
)
If you don't understand something it's quite easy to find something good on the internet because there are several great batch scripting sites. Further you can always use echo This is a message visible on the command prompt to display something that might be useful e.g. variables etc. With that you can "debug" and look what happens.
Some explanations beyond the comments (rem This is a comment) in the code:
1.
break command:
To clear a file I use the break command which will produce no output at all. That empty output I redirect to a file, read it here: https://stackoverflow.com/a/19633987/8051589.
2.
General variables:
You set variables via set varname=Content I prefer the way as I do it with quotes: set "varname=Content" as it works with redirection characters also. Use the variable with one starting % and one trailing % e.g. echo %varname%. You can read a lot of it on https://ss64.com/nt/set.html. I think ss64 is probably the best site for batch scripting out there.
3.
Redirection > and >>:
You can redirect the output of a command with > or >> where > creates a new file and overwrites existing files and >> appends to a file or create one if not existing. There are a lot more thing possible: https://ss64.com/nt/syntax-redirection.html.
4.
for /f loop:
In a batch file you loop through the lines of a command output by using a for /f loop. The variable that is used will be written with 2 % in front of it, here %%a. I also set the delimiter delimns to nothing so that the command output will not be separated into several tokens.
You can read a lot of details about a for /f loop at: https://ss64.com/nt/for_cmd.html.
5.
Special variable syntax %%~xa and %~dp0:
The variable %%a which hold one line of the dir command can be expand to the file extension only via: %%~xa as explained here: https://stackoverflow.com/a/5034119/8051589. The %~dp0 variable contains the path where the batch file is located see here: https://stackoverflow.com/a/10290765/8051589.
6.
Block redirection ( ... )>>:
To redirect multiple commands at once you can open a block (, execute commands, close the block ) and use a redirection. You could also execute every command and redirect that only that would have the same effect.

How do you loop inside sub directories in windows, and repeat command using windows batch scripting

I am on Windows 10. I have a folder with sub folder. All subfolders have "Subs" folder inside them. I want to loop through all subdirectories, goto Subs directory, unrar a file, Goto next subdirectory and repeat.
I tried below script but could not get to work.
#echo off
setlocal enableextensions enabledelayedexpansion
set "rootFolder=C:\Users\MNM\MAT"
echo ----------------------------------------------------------------------
for /d /r "%rootFolder%" %%a in (.) do (
set mypath=%cd%
#echo %mypath%
cd %%a
set mypath=%cd%
#echo %mypath%
cd Subs
set mypath=%cd%
#echo %mypath%
C:\Users\MNM\MAT\unrar e *subs.rar C:\Users\MNM\mat2\
cd C:\Users\MNM\MAT
)
This simple task can be done with just a single command line:
#echo off
for /R "%USERPROFILE%\MAT" %%I in ("*subs.rar") do "%USERPROFILE%\MAT\UnRAR.exe" x -c- -idcdp -y "%%I" "%USERPROFILE%\mat2\"
USERPROFILE is a predefined Windows Environment Variable which is on your computer for your user account defined with value C:\Users\MNM.
The command FOR searches in directory C:\Users\MNM\MAT and all its non hidden subdirectories because of /R for non hidden files matching the pattern *subs.rar. Each file name found is assigned with full path to loop variable I.
UnRAR is executed for each found RAR archive file for extracting the archive to directory C:\Users\MNM\mat2 with extracting also the directory structures inside the RAR archive file because of command x instead of e. Existing files in destination directory (tree) are automatically overwritten because of -y. The switches -c- and -idcdp are for displaying less information during extraction process.
For a brief description of used and additionally available switches of UnRAR run in command prompt window UnRAR without any parameter or with /? as parameter. A complete description of the commands and switches of UnRAR can be found in text file Rar.txt in program files folder of WinRAR if that shareware application is also installed and not just the freeware UnRAR.
It is absolutely not needed to change into the directory containing the RAR archive file on extracting all RAR archives into same destination directory as it can be seen here.
This is one possible way if I understood your folder structure correctly:
#echo off
set "Base=C:\Users\MNM\MAT"
echo ----------------------------------------------------------------------
for /F "delims=" %%A in (
'dir /B/S "%Base%\*subs.rar" ^| findstr /i "^%Base:\=\\%\\[^\\]*\\Subs\\[^\\]*subs.rar$"'
) do Echo "C:\Users\MNM\MAT\unrar.exe" e "%%~fA" "C:\Users\MNM\mat2\"
the for /f will parse the output of the dir and findstr
dir will iterate all *subs.rar in the tree starting from %Base%
the complex RegEx in findstr will filter the rar's to those in a folder subs in a subfolder of %Base%
as a backslash is an escape char in a RegEx, literal backslashes have to be doubled.
If the output looks ok remove the echo in the last line.
Just because recursing all subdirectories and extracting all *subs.rar files wasn't requested here's an example that is based upon my assumptions:
#ECHO OFF
SET "rootDir=%USERPROFILE%\MAT"
IF /I NOT "%CD%"=="%rootDir%" CD /D "%rootDir%"
FOR /D %%A IN (*
) DO IF EXIST "%%A\Subs\*subs.rar" UNRAR e "%%A\Subs\*subs.rar" mat2\

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