Perhaps someone can be of help; I have several files with the following naming convention:
fooR1.txt, fooR2.txt, fooR3.txt, . . . , fooR1000.txt
I wish to delete all the files greater than R500. I have several folders and I know how to pass through each folder, but I am not sure how to capture and delete the files with replication 501 and greater. How can I do such?
How about simply:
ren foo500.txt foo499bis.txt
del fooR5??.txt fooR6??.txt fooR7??.txt fooR8??.txt fooR9??.txt fooR10??.txt
ren foo499bis.txt foo500.txt
Not elegant, but efficient.
This will delete all files fooR###.txt where ### is greater than 500.
#echo off
setlocal EnableDelayedExpansion
for %%f in (fooR*.txt) do (
set num=%%~f
set num=!num:~4,-4!
if !num! gtr 500 del /q "%%~f"
)
endlocal
Because your range is open, I've reversed your criteria: delete anything that is not in the range 1-499. Please be aware that this is not exactly equivalent to yours, for example it will also delete a file named fooR001.txt or fooR_something_else.txt
It's also pretty slow.
#echo off
for %%F in (fooR*.txt) do (
echo %%F | findstr /v /r "fooR[1-9]\.txt fooR[1-9][0-9]\.txt fooR[1-4][0-9][0-9]\.txt" >nul && echo del %%F
)
First line (for) enumerates files starting with fooR, then for each file findstr checks if it does not match pattern (/v option) and finally a command is executed if a check (ie does not match) is positive (&& means execute only if previous command was successfull).
Code above will just echo commands, not execute them, so you may safely run it to verify it actually behaves as it should. To actually run delete, just remove echo in front of it.
note: you could actually run this directly from command line in a form of:
#for %F in (fooR*.txt) do #echo %%F | findstr /v /r "fooR[1-9]\.txt fooR[1-9][0-9]\.txt fooR[1-4][0-9][0-9]\.txt" >nul && echo del %F
You would need to make a Batch script for this. Then in the Batch file you could write.
DEL "fooR500.txt"
To delete all files with a .txt ending you would just write:
DEL "*.txt"
That's all I know, but if you want to get it so it does files 500 and higher you would have
to create a variable in Batch that holds the value 500 using:
set Value = 500
and then have it delete file "fooR" + Index + ".txt" so to do that you would have to do:
set "FilePre = fooR"
set "FileW = %FilePre% %Value%"
set "Ex = .txt"
set "FileX = %FileW% %Ex%"
del FileX
Then you will have to make Value go up by one and repeat the process 500 times until it reaches 1000.
Related
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.
I have a folder that contains files; each document should have .pdf and .xml format. I need to write a BAT file to run from a scheduled task to verify that both documents exist for each.
My logic is:
loop through files in the folder
strip each file to its name without extension
check that same name files exist for both .xml and pdf.
if not mark a flag variable as problem
when done, if the flag variable is marked, send an Email notification
I know how to use blat to sending email, but I'm having trouble to execute the loop. I found a way to get path and file name without extension but can't merge them.
I've used batch files a few time, before but I'm far from an expert. What am I missing?
Here's the code I have so far:
set "FolderPath=E:\TestBat\Test\"
echo %FolderPath%
for %%f in (%FolderPath%*) do (
set /p val=<%%f
For %%A in ("%%f") do (
Set Folder=%%~dpA
Set Name=%%~nxA
)
echo Folder is: %Folder%
echo Name is: %Name%
if NOT EXIST %FolderPath%%name%.xml
set flag=MISSING
if NOT EXIST %FolderPath%%name%.pdf
set flag=MISSING
)
echo %Flag%
pause
There is no need for fancy code for a task such as this:
#Echo Off
Set "FolderPath=E:\TestBat\Test"
If /I Not "%CD%"=="%FolderPath%" PushD "%FolderPath%" 2>Nul||Exit/B
Set "flag="
For %%A In (*.pdf *.xml) Do (
If /I "%%~xA"==".pdf" (If Not Exist "%%~nA.xml" Set "flag=MISSING")
If /I "%%~xA"==".xml" (If Not Exist "%%~nA.pdf" Set "flag=MISSING")
)
If Defined flag Echo=%flag%
Timeout -1
Something like this :
set "FolderPath=E:\TestBat\Test\"
pushd "%FolderPath%"
for %%a in (*.xml) do (
if exist "%%~na.pdf"(
echo ok
) else (
rem do what you want here
echo Missing
)
)
popd
Is this what you want?
#echo off
setlocal enabledelayedexpansion
set "FolderPath=E:\TestBat\Test\"
echo !FolderPath!
for /f "usebackq delims=" %%f in (`dir !FolderPath! /B`) do (
set /p val=<%%f
For %%A in ("%%f") do (
Set Folder=%%~dpA
Set name=%%~nxA
)
echo Folder is: !Folder!
echo Name is: !name!
if NOT EXIST !FolderPath!!name!.xml set flag=MISSING
if NOT EXIST !FolderPath!!name!.pdf set flag=MISSING
)
echo Flag: !flag!
pause
endlocal
You should reformat your code and keep in mind that the grama for batch file is critical. BTW, if you are trying to update the existing batch variable and read it later, you should enable localdelayedexpansion and use ! instead of %.
Keep it simple:
#echo off
pushd "E:\TestBat\Test" || exit /B 1
for %%F in ("*.pdf") do if not exist "%%~nF.xml" echo %%~nxF
for %%F in ("*.xml") do if not exist "%%~nF.pdf" echo %%~nxF
popd
This returns all files that appear orphaned, that is, where the file with the same name but the other extension (.pdf, .xml) is missing. To implement a variable FLAG to indicate there are missing files, simply append & set "FLAG=missing" to each for line and ensure FLAG is empty initially. Then you can check it later by simply using if defined FLAG.
Note: This does not cover the e-mail notification issue. Since I do not know the BLAT tool you mentioned, I have no clue how you want to transfer the listed files to it (command line arguments, temporary file, or STDIN stream?).
In case there is a huge number of files in the target directory, another approach might be better in terms of performance, provided that the number of file system accesses is reduced drastically (note that the above script accesses the file system within the for loop body by if exist, hence for every iterated file individually). So here is an attempt relying on a temporary file and the findstr command:
#echo off
pushd "E:\TestBat\Test" || exit /B 1
rem // Return all orphaned `.pdf` files:
call :SUB "*.pdf" "*.xml"
rem // Return all orphaned `.xml` files:
call :SUB "*.xml" "*.pdf"
popd
exit /B
:SUB val_pattern_orphaned val_pattern_missing
set "LIST=%TEMP%\%~n0_%RANDOM%.tmp"
> "%LIST%" (
rem // Retrieve list of files with one extension:
for %%F in ("%~2") do (
rem /* Replace the extension by the other one,
rem then write the list to a temporary file;
rem this constitutes a list of expected files: */
echo(%%~nF%~x1
)
)
rem /* Search actual list of files with the other extension
rem for occurrences of the list of expected files and
rem return each item that does not match: */
dir /B /A:-D "%~1" | findstr /L /I /X /V /G:"%LIST%"
rem // Clean up the temporary file:
del "%LIST%"
exit /B
To understand how it works, let us concentrate on the first sub-routine call call :SUB "*.pdf" "*.xml" using an example; let us assume the target directory contains the following files:
AlOnE.xml
ExtrA.pdf
sAmplE.pdf
sAmplE.xml
So in the for loop a list of .xml files is gathered:
AlOnE.xml
sAmplE.xml
This is written to a temporary file but with the extensions .xml replaced by .pdf:
AlOnE.pdf
sAmplE.pdf
The next step is to generate a list of actually existing .pdf files:
ExtrA.pdf
sAmplE.pdf
This is piped into a findstr command line, that searches this list for search strings that are gathered from the temporary file, returning non-matching lines only. In other words, findstr returns only those lines of the input list that do not occur in the temporary file:
ExtrA.pdf
To finally get also orphaned .xml files, the second sub-routine call is needed.
Since this script uses a temporary file containing a file list which is processed once by findstr to find any orphaned files per extension, the overall number of file system access operations is lower. The weakest part however is the for loop (containing string concatenation operations).
I'm using REN to find files with a certain naming pattern and modify them, like so:
REN "?%var1%?%var2%.S16" "?%var1%?%var3%.S16"
This finds all files like aXaY.S16, bXaY.S16, cXbY.S16 (etc) and renames them to aXaZ.S16, bXaZ.S16, cXbZ.S16 (etc). If it finds what it's looking for, it works just fine. But there's a problem: REN won't halt the operation if it encounters an error.
To prove this is the case, my script is as follows:
#echo off
set /p var1=Enter first var:
set /p var2=Enter second var:
set /p var3=Change second var to:
echo Searching for all files matching ?%var1%?%var2%.S16
REN "?%var1%?%var2%.S16" "?%var1%?%var3%.S16"
echo Errorlevel: %errorlevel%
IF ERRORLEVEL 1 goto :FAIL
echo Success!
PAUSE
goto :eof
:FAIL
echo I done goofed!
PAUSE
exit
I ran this in a folder containing a few hundred files. I searched for files matching ?0?a.S16 (of which there are ~40 results) and asked it to change the 'a' to a 'c', knowing that files with this name already exist and should create a conflict.
Here is the console output (shortened for brevity):
Enter first var: 0
Enter second var:a
Change second var to:c
Searching for all files matching ?0?a.S16
A duplicate file name exists, or the file
cannot be found.
A duplicate file name exists, or the file
cannot be found.
A duplicate file name exists, or the file
cannot be found.
...(etc)...
Errorlevel: 1
I done goofed!
Press any key to continue . . .
The complaint about duplicates goes on for about 40 lines, as expected. As you can see, though, Errorlevel returns 1 at the end of the process instead of quitting at the first sign of trouble, which is what I'd rather it do.
I did consider passing this through FOR /f but I don't know how I would also pass the searchmask through it. I know FOR supports '*' wildcards, but as far as I'm aware, it doesn't support '?' the same way REN does. A possible alternative would be to use regular expressions somehow, but I can't wrap my head around them at all despite trying.
Any clues? Many thanks for taking a look.
Yes, you have documented how the REN command works - it continues to completion, even after a rename fails, and then reports ERRORLEVEL 1 if at least one rename failed.
If you want to halt processing upon the first error, then you will have to write your own loop to rename each file, one at a time. You should not use the simple FOR loop because it can begin iterating before it has scanned the entire directory, so you run the risk of renaming the same file twice. The safe thing to do is use FOR /F coupled with DIR /B /A-D instead.
#echo off
set /p var1=Enter first var:
set /p var2=Enter second var:
set /p var3=Change second var to:
echo Searching for all files matching ?%var1%?%var2%.S16
for /f "eol=: delims=" %%F in (
'dir /b /a-d "?%var1%?%var2%.S16"'
) do ren "%%F" "?%var1%?%var3%.S16" || goto :break
:break
echo Errorlevel: %errorlevel%
IF ERRORLEVEL 1 goto :FAIL
echo Success!
PAUSE
goto :eof
:FAIL
echo Errorlevel: %errorlevel%
echo I done goofed!
PAUSE
exit
Note - The wildcard rules used by REN are not at all intuitive. You should have a look at How does the Windows RENAME command interpret wildcards? to make sure you are getting the results you expect.
replace you ren command with
FOR /f "delims=" %%x IN ('dir /b /a-d "%sourcedir%\?%var1%?%var2%.S16" 2^>nul') DO REN "%sourcedir%\%%x" "?%var1%?%var3%.S16"&IF ERRORLEVEL 1 ECHO failed AT %%x&goto :EOF
note This is a direct patch of my test, where I set sourcedir to a testing directory. In your case, you'd need to make appropriate adjustments.
This makes a directory list in basic form without directories and each filename is then assigned to %%x Then rename is then attempted and the resultant errorlevel interpreted. On fail, show the name for good measure and bail out.
[edit] 2^>nul added into dir command.
This will cause dir errors (like "file not found") to be directed to nowhere. The caret (^) is required to tell cmd that the > is part of the dir command, not the for.
The application creates .mta files (with exactly same name) of all multimedia files in my HDD. What I want to do is to check all sub-folders of root folder if there is no multimedia file associated with some .mta then delete it.
Detailed example. Lets say we have files
01.mp3
01.MP3.mta
02.mkv
02.MKV.mta
03.jpg
03.JPG.mta
04.MP4.mta <<==
As you see the last .mta has no original file. I want to delete last file.
I don't know if it's possible with cmd. But following function doesn't work. Please take a look
For /r %%i in (*.mta) do call :nomta %%i
pause
goto end
:nomta
set stem=%1:.mta=%
set original=%stem%.mta
if not exist %original% do exit /B
if not exist %stem% do del /a /Q %1
goto :EOF
:end
echo done
PAUSE
You can use a for command to do this with dir /a feeding it both hidden and non-hidden filenames. Here's an example:
C:\temp\z\z>attrib *
A C:\temp\z\z\foo.bar.mta
A H C:\temp\z\z\h2.mp4.mta
A C:\temp\z\z\hid.mp4
A H C:\temp\z\z\hid.mp4.mta
A C:\temp\z\z\zoo.bar
A C:\temp\z\z\zoo.bar.mta
C:\temp\z\z>for /f %F in ('dir /b/a *.mta') do if not exist "%~nF" echo %F >> z
C:\temp\z\z>type z
foo.bar.mta
h2.mp4.mta
so replacing the echo with a del should achieve your target.
I have been reading great posts in this forum and got close to what I want to do but couldn't figure out the exact code.
I want to create a windows batch file to do following:
Perform a looped search for each line item of a text file (this is a list of keyword) to locate files in a a specific directory
For this search partial match is okay.
Each time a file is found, move it to a predefined directory (e.g. C:\temp\search_results)
Thanks.
I'm not running Windows at the moment, so I can only post some ideas, not the solution.
1) Use for /f to iterate over file contents.
2) Use find "%Keyword%" %SourceDir% to get the list of matching files. You will have to parse out file names from the output of find.
2a) As an alternative, you can iterate over files in the source dir (with nested for) and call find for each file, discarding its output and using its exit code (%ERRORLEVEL%) to decide whether the file matches (it will return 0 if there is a match and nonzero if there is no match). Something like this:
for %%F in (%SourceDir%\*) do (
find "%Keyword%" %%F > nul
if not errorlevel 1 (echo File %%F matches) else (echo File %%F does not match)
)
3) Move matching files with move.
There are multiple problems.
FIND /i "%A%" ... can't work, the name of the FOR-Varibale is %%A
And the second proble: With FIND you check the content of the file not the name.
And you should use indention to avoid too much parenthesis.
You better try
FOR /F "tokens=*" %%A IN (%listfile%) DO (
FOR %%f in (%searchdir%\*) do (
set "filename=%%~f"
set replaced=!filename:%%A=!
if !replaced! NEQ !filename! (
echo !filename! contains '%%A'
)
)
)
It tries to replace %%A inside of the filename with .
If the replaced is not equal the filename, the filename must contain %%A
I wrote the following code but not sure if I am in the right track. Here is my setup:
list.txt file contents are (my keywords for the filename search) --
one
two
five
ten
six
f1 folder contains --
four.txt
one.txt
three.txt
I want to move the matching ones to F2 folder, but the code simplicity I am using echo instead.
My code is:
#ECHO OFF
SETLOCAL EnableDelayedExpansion
SET listfile=D:\batchtest\list.txt
SET searchdir=D:\batchtest\f1
FOR /F "tokens=*" %%A IN (%listfile%) DO (
FOR %%f in (%searchdir%\*) do (FIND /i "%A%" %%f
if errorlevel 1 (
echo Search failed) else (
echo Search successful
)
)
)
)
It is running but not finding matching filenames.
Thanks.