Batch file to sort files and list missing - windows

I am trying to write a batch file that would read rows/lines from a txt file containing a list. The batch file would then copy the documents that match, and produce a list of missing files.
So far, the code successfully copies the files that it matches, but it also fills the "Missing.txt" file will exact contents of the input list, rather than simply the missing files.
#echo off
::Requests name of list file to be used by batch file
echo Enter list file name and press enter
set /p var=
mkdir %userprofile%\Desktop\%var%\
set /A lis=1
::Logic to search for files based on contents of list inputted by user at start.
for /f "tokens=*" %%i in (%var%.txt) DO (
call :processline %%i
IF NOT EXIST %%i (echo %%i>>%userprofile%\Desktop\%var%\Missing.txt)
)
pause
::Function called processline
::Assigns a string/value to variable "line"
::Copies a file with name = "line" to the user's desktop
::Renames the file to include a number reference, based on original list being searched
::Increments number for next file to be searched,copies and renamed
:processline
echo line=%*
xcopy /s %*.* %userprofile%\Desktop\%var%
move /Y %userprofile%\Desktop\%var%\%*.pdf %userprofile%\Desktop\%var%\%lis%_%*.pdf
set /A lis=%lis%+1
:eof
I suspect my problem is within the "for" logic, although there might be a way to input missing file names within the processline function.
Any help or advice would be much appreciated.

It's command order: :processline subroutine moves something, maybe including %%i file. I'd use next code snippet:
IF EXIST "%%~i" (
call :processline %%i
) ELSE (
>>"%userprofile%\Desktop\%var%\Missing.txt" echo %%i
)

Related

Batch Create Several Folders Based on Multiple Filenames, and Move Multiple Related Files to The Created Folders

Every week, one of my co-workers has had to go through a folder with hundreds of demuxed video and audio files, rename each one individually for a specific city TV station and then sort them into folders based on the name of the city. I've created a .bat file to rename them all for him, and now I'd like to create a .bat file that creates new directories based on the filenames, and places the corresponding files into the new folders. I copied a few of the files to test with.
So the end result will be a "Houston" folder with all it's corresponding files, a "Compton" folder with it's files, a "Moline" folder, etc, etc... for every city, up to around 200 cities, and we're only getting more.
He's currently searching "Houston", cutting all the files that come up, creating a new folder manually, naming it "Houston" and pasting all the files into his new folder. FOR EVERY CITY. 200 TIMES. And it takes hours.
The files are ALWAYS named with this system: X### Random City, ST
With my little wee programming knowledge, I'm supposing that the script could detect all the characters after the first space, and before the comma, copy those characters (Random City), create a new folder, name it the copied characters (Random City) then move any files containing "Random City" in their filename into the newly created folder. The end result would be as such, just with a lot more folders.
Is there anyone more advanced than me who could explain the best way to to this?
I apologize in advance if I'm in the wrong place or not savvy enough. Cheers!
UPDATE: I messed around, learned about tokens and delimiters, variables etc. Here is what I have which works amazingly, except I'm not sure how to remove the comma at the end of the city name. I'm using space as the delimiter, which makes the text chunks the tokens if I understand correctly, including my comma, using tokens=2. Another problem that arises; Say there's a city with two text chunks (tokens) eg. San Fransisco, Baton Rouge. How could I grab both of them, using the comma as my stopping point? My code is below.
#echo off
setlocal enabledelayedexpansion
for %%A in (*.m2v *.mpa) do (
echo file found %%A
for /f "delims=" %%B in ("%%A") do set fname=%%~nB
for /f "delims=" %%C in ("%%A") do set fextn=%%~xC
for /f "tokens=2* delims= " %%D in ("!fname!") do set folname=%%D
echo folder name !folname!
if not exist "!folname!" (
echo Folder !folname! doesn't exist, creating
md "!folname!"
) else (
echo Folder !folname! exists
)
echo Moving file %%A to folder !folname!
move "%%A" "!folname!"
)
echo Finished
pause
UPDATE 2: I found a meh workaround to get rid of the comma, by adding it as a delimiter, but I'm still trying to wrap my head around the 2 word cities. My Baton Rouge and San Fransisco folders are being named respectively, "Baton" and "San". Here is my code so far, I'll update if I find a better way.
setlocal enabledelayedexpansion
for %%A in (*.m2v *.mpa) do (
echo file found %%A
for /f "delims=" %%B in ("%%A") do set fname=%%~nB
for /f "delims=" %%C in ("%%A") do set fextn=%%~xC
for /f "delims=," %%B in ("%%A") do set fname=%%~nB
for /f "tokens=2* delims= " %%D in ("!fname!") do set folname=%%D
echo folder name !folname!
if not exist "!folname!" (
echo Folder !folname! doesn't exist, creating
md "!folname!"
) else (
echo Folder !folname! exists
)
echo Moving file %%A to folder !folname!
move "%%A" "!folname!"
)
echo Finished
pause
UPDATE 3
Here is my code which worked. However, if the number of characters in your filename prefixes/suffixes changes, it will screw things up and you'll have to edit code.
#ECHO OFF
SETLOCAL enabledelayedexpansion
FOR %%A in (*.m2v *.mpa) do (
ECHO file found %%A
FOR /F "delims=" %%B in ("%%A") do set fname=%%~nB
SET folname=!fname:~5,-4!
ECHO folder name !folname!
if not exist "!folname!" (
ECHO Folder !folname! doesn't exist, creating
MD "!folname!"
) else (
ECHO Folder !folname! exists
)
ECHO Moving file %%A to folder !folname!
MOVE "%%A" "!folname!"
)
ECHO Finished
PAUSE
Using the SET folname=!fname:~5,-4!allows me to trim the M373 prefix, 5 characters in, and the , TX suffix, 4 characters in, removing the comma and salvaging the city name, regardless of how long it is, or how many words it is (eg. West Palm Beach, FL) . Antares mentioned this solution in his answer which worked like a charm.
BUT IT ALSO MADE ME THINK
If the number of characters in the prefix changes, which is likely, I'll either have to edit the batch file every time, or create a specific batch file for each circumstance. Not terrible, but not great either. So I went with Michael Heath's answer which works flawlessly. I'm not smart enough yet to know exactly why, but I'm gonna dissect it and find out. I have a lot of learning to do. Thanks, everyone!
#echo off
setlocal
rem A=Fullpath, B=Name before comma, C=B prefix, D=B without prefix.
for /f "delims=" %%A in ('dir /b *.m2v *.mpa') do (
for /f "delims=," %%B in ("%%~nA") do (
for /f "tokens=1,*" %%C in ("%%~B") do (
if not exist "%%~D\" (
echo Folder "%%~D" doesn't exist, creating
md "%%~D"
)
if exist "%%~D\" (
echo Moving file "%%~A" to folder "%%~D\"
move /y "%%~A" "%%~D\"
) else echo Folder "%%~D\" doesn't exist
)
)
)
echo Finished
pause
3 for loops to get the tokens needed.
1st will get the fullpath.
2nd to get the name before the comma.
3rd to get the name without the prefix.
In the nested for loops check if folder exists, create it if not. Then if folder exists, move file inside.
Here is my code which worked. However, if the number of characters in your filename prefixes/suffixes changes, it will screw things up and you'll have to edit code.
#ECHO OFF
SETLOCAL enabledelayedexpansion
FOR %%A in (*.m2v *.mpa) do (
ECHO file found %%A
FOR /F "delims=" %%B in ("%%A") do set fname=%%~nB
SET folname=!fname:~5,-4!
ECHO folder name !folname!
if not exist "!folname!" (
ECHO Folder !folname! doesn't exist, creating
MD "!folname!"
) else (
ECHO Folder !folname! exists
)
ECHO Moving file %%A to folder !folname!
MOVE "%%A" "!folname!"
)
ECHO Finished
PAUSE
Using the SET folname=!fname:~5,-4!allows me to trim the M373 prefix, 5 characters in, and the , TX suffix, 4 characters in, removing the comma and salvaging the city name, regardless of how long it is, or how many words it is (eg. West Palm Beach, FL) . Antares mentioned this solution in his answer which worked like a charm.
BUT IT ALSO MADE ME THINK
If the number of characters in the prefix changes, which is likely, I'll either have to edit the batch file every time, or create a specific batch file for each circumstance. Not terrible, but not great either. So I went with Michael Heath's answer which works flawlessly. I'm not smart enough yet to know exactly why, but I'm gonna dissect it and find out. I have a lot of learning to do. Thanks, everyone!
Before Image
After Image
I can give you some hints. If you have a more specific problem case, feel free to update your question again.
You can cut the last X characters of a String like this: %variablename:~0,-X%
If you know the variables with the city parts, e.g. %%D and %%E or something, you can concatenate them again like this md "%%D %%E". However, this works just for a fixed number of tokens, like the two here.
You can store this concatenations in an own variable, if you need the result outside of your for-loop. Use set myVariable=%%D %%E for example, and show it with %myVariable% or !myVariable! (when delayed expansion is needed), for example md "%myVariable%".
A nifty workaround: if there are only a small number of "special cities" to take into consideration, then you could just add some rename commands at the end of your script, like rename San "San Francisco", rename Baton "Baton Rouge", etc. Will not work well, if there are more "San" cities (e.g. "San Bernadino"), because this cannot be distinguished anymore. But in this case, the copying into separate folders would already fail as well.
In your script you make a check for an existing folder. I think you can omit that. md or mkdir either create that directory or do nothing if it exists. Well, they do print a message to the console, which can be ignored. If you do not want to see them, redirect the error message stream to nul like this md myFolder 2>nul. This will swallow any error messages, but it is unlikely that you get any other error message than that in your scenario.
You could simplify your approach like this: I reckon your file renaming works well. Your "copy" script could be just a list of commands which are stated explicitly (and also could be edited fairly quickly if new cities are to be considered).
Set the batch file up like this with entries for each city:
#echo off
mkdir "Moline"
copy "*Moline*.*" "Moline"
mkdir "San Francisco"
copy "*San Francisco*.*" "San Francisco"
...
echo done.
Side effect is, that the folders for each city will be created, not just those where files are copied into. May be it suits your needs anyhow.
Also, I would like to give you pointers to sources of help/documentation:
On the command line you can get extensive help by executing the commands used in your batch file and appending /?. For example set /? gives you a lot of useful things you can do with variables/String manipulations.
Try: for /?, if /? (regarding errorlevel for example), maybe call /?, goto /?, and others.
A very good source for all the command line commands is https://www.ss64.com. This site provides extensive help even for PowerShell and Linux Bash and others. Relevant to you in this case would be CMD, direct link: https://ss64.com/nt/
Edit/Update:
Check out symbol replacement on the set command to build something like
if "%myVariable:~0,1%" == "M" (set myvariable=%myvariable:~1%)
The first part "cuts" the first character and checks, if it is an M and if so, it keeps everything except the first character. With this you could make your filenames even out to process them inside your batch file.
You can also "remove" a letter or substring with %myVariable:, TX=% which would replace any ", TX" occurance with "nothing" for example.
Oh, this could also help to remove any spaces in the filename. Then you could extract the "SanFrancisco" without a spaces problem ;) The folder name would be without space though. This could be resolved with further rename commands at the end.
Here's an alternative method, just for the sake of variety:
#Echo Off
SetLocal DisableDelayedExpansion
Set "SourceDir=.\Batch Rename\BACKUP"
If Exist "%SourceDir%\" For /F "EOL=|Delims=" %%G In (
'%__AppDir__%where.exe "%SourceDir%":"*, ??.m??" 2^>NUL'
)Do (Set "FileBaseName=%%~nG"&SetLocal EnableDelayedExpansion
For /F "EOL=|Delims=," %%H In ("!FileBaseName:* =!")Do (EndLocal
%__AppDir__%Robocopy.exe "%%~dpG." "%%~dpG%%H" "%%~nxG" /Mov>NUL))
This method filters your files with the where command. It selects for moving, only file names which end with a comma, followed by a space, followed by two letters, followed by a three letter extension beginning with the character m. It moves the files, automatically creating the destination directories if they do not exist, using the robocopy command. It uses only two for loops, the second of which, isolates the string between the first space and the next comma.
I have made it so that the script can be located anywhere, not necessarily in the directory with the files. This location is set on line 3 of the script, If you wish to modify it, please ensure that your location remains between the = and the closing double-quote, ", and does not end with a trailing back-slash, \. It is currently set to a relative directory, (based upon that visible in your screen-shot), but you could obviously use an absolute path too, e.g. Set "SourceDir=C:\Users\UserName\Videos". If you wish to keep the script in the same directory as the files to be moved, change it to read Set "SourceDir=." and just double-click it to run.
When I had to develop a script for the task at hand I would probably implement a few safety features in order to not move wrong files. Your sample data show pairs of .m2v and .mpa files, but I would likely not consider that as granted. Also would I not rely on a fixed-length prefix. Finally, I would perhaps also account for lit's comment.
So here is my attempt (see all the explanatory rem-remarks in the code):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=%~dp0." & rem // (root directory containing the files to be processed)
set "_MASK=M??? *, ??.m2v" & rem // (mask to find the files to be processed)
set _EXTS=".m2v" ".mpa" & rem /* (list of extensions that must all be present;
rem extensions are not checked if this is empty;
rem `_MASK` should then be changed to end with `.m*`) */
set "_FILT=^M[0-9][0-9][0-9] [^,][^,]*, [A-Z][A-Z]\.[^\.][^\.]*$"
rem // (additional filter to find files; deactivate by `.*`)
set "_SEPS=," & rem /* (defines (a) separator character(s) to derive the
rem sub-directory; leave it blank to use full name) */
rem // Change into the root directory:
pushd "%_ROOT%" && (
rem /* Loop through all files matching the mask as well as the additional filter;
rem if the post-filtering is not needed, remove `^|` and everything behind: */
for /F "delims= eol=|" %%F in ('
dir /B /A:-D-H-S "%_MASK%" ^| findstr /I /R /C:"%_FILT%"
') do (
rem // Get the portion in front of the `,` of the file name:
for /F "delims=%_SEPS% eol=|" %%G in ("%%~nF") do (
rem // Split that portion at the first space to get the city name:
for /F "tokens=1* eol=|" %%H in ("%%G") do (
rem // initialise flag that indicates whether to move the current file:
set "FLAG=#"
rem // Skip the following checks if there are no extensions defined:
if defined _EXTS (
rem // Loop through the extensions in the list:
for %%E in (%_EXTS%) do (
rem /* Reset flag if file with current name and iterated extension
rem cannot be found; this ensures that files with all listed
rem extensions do exist, otherwise no files are moved: */
if not exist "%%~nF%%~E" set "FLAG="
rem /* Reset flag if file with current name and iterated extension
rem is actually a directory (though this is very unlikely): */
rem if exist "%%~nF%%~E\*" set "FLAG="
rem /* Reset flag if file with current name and iterated extension
rem is already located in the target sub-directory: */
if exist "%%I\%%~nF%%~E" set "FLAG="
)
)
rem // Do the following steps only if the flag has not been reset:
if defined FLAG (
rem // Create target sub-directory (suppress potential error message):
2> nul md "%%I"
rem // Check if there are dedicated extensions defined:
if defined _EXTS (
rem // Loop through the extensions in the list again:
for %%E in (%_EXTS%) do (
rem /* Move file with current name and iterated extension;
rem nothing is overwritten due to the preceding checks: */
> nul move "%%~nF%%~E" "%%I\%%~nF%%~E"
)
) else (
rem /* Empty list of extensions, hence just move the current file;
rem if you do want to overwrite, remove the `if exist´ part: */
if not exist "%%I\%%F" > nul move /Y "%%F" "%%I\%%F"
)
)
)
)
)
rem // Return from root directory:
popd
)
endlocal
exit /B
The values in the Define constants here: section at the top of the script are defined to suit your sample data, but they can easily be adapted there to configure the script at one place:
_ROOT: points to the directory where your input files are; %~dp0. points to the parent directory of the script, but you may of course specify any other absolute directory path here;
_MASK: is a file pattern that matches one file per pair (only .m2v files, others are covered by _EXTS); M??? matches the four-character prefix, but you can change it to M?*, for instance, to also match prefixes like M1 or M9999; if you do so, however, also edit _FILT accordingly;
_EXTS: defines a list of extensions that all must be present; that means for a certain base file name (like M372 Houston, TX, there must exist a file per each given extension, hence M372 Houston, TX.m2v and M372 Houston, TX.mpa in our situation, otherwise these files are not going to be moved; if you do not care if such a pair is complete or not, simply state set "_EXTS=" (so clear it) and change the extension of _MASK from .m2v to .m*, so all files with an extension beginning with .m are moved;
_FILT: constitutes an additional filter for file names in order to exclude wrong files; this currently also reflects a four-character prefix, but if this is not always the case, just change M[0-9][0-9][0-9] to M[0-9]*; if you do not want to filter, set this to .*, so it matches everything;
_SEPS: defines the character(s) to split the base file name in order to derive the respective sub-directory, so everything ending before that character and beginning after the first SPACE is the resulting sub-directory name; if you do not define a character here, the whole remaining base file name (so everything after the first SPACE until but not including the (last) .) is taken;

Trying to make a menu in a windows command prompt

I have a batch file that gets run by the user typing:
usercompile filename
usercompile is a batch file that does this:
copy /y %1.txt lib\incoming_file.txt
and then starts the compiler:
compiler.exe
The compiler has the "incoming_file" name hard-coded into linked source (this can't be chaged), so the current method is simply to copy the user file in and rename it to the known name and run the compiler.
I'd like to present the user with a list of files that are generated when a batch file is run, then the batch file would copy the selected file in, rename it (just like is done now).
So it would look like this:
Please choose a file to compile:
1) matthews_build
2) marks_build
3) lukes_build
and then the user would type 1 or 2 or 3 (in this case) and press enter. The batch file would copy that file to the known file name and launch the compiler. The one good thing is that the files that need to be listed all have a unique extension (.jal).
Any ideas?
I changed my approach and consider my previous answer a bad practice: re-listing the files with a second dir command unnecessarily reads the disk again, not to mention the rare but possible case if a file is added/removed between the 2 dir's and makes the whole thing unreliable.
Based on this brilliant solution I did a possible implementation with dynamic array:
#echo off
set /a counter=0
setlocal enabledelayedexpansion
FOR /f "delims=|" %%i IN ('dir /b /on "yourpath*.jal"') DO (
set /a counter+=1
rem echo !counter!^) %%~ni
set FileList[!counter!]=%%~ni & rem This is an array element, a dinamically created variable
)
rem Iterate through variables:
FOR /l %%i IN (1,1,!counter!) DO (
echo %%i^) !FileList[%%i]!
)
set /p option="Choose an option: "
echo !FileList[%option%]!
endlocal
This makes the file list available for any number of following commands.
One possible solution is to list all .jal files and give them an option number, store the result, and based on user input, look up the file based on the option number. As I know no way of storing such a result in memory (no array/hash table data type), only in a file, if a file can not be used, then the listing should be repeated in a deterministic way so that if we re-assign the option numbers, we get the same result. We can do it ensuring alphabetical ordering.
Here is one implementation:
BLOCK 1
setlocal enabledelayedexpansion
FOR /f "delims=|" %%i IN ('dir /b /on "yourpath\*.jal"') DO (
set /a counter+=1
echo !counter!^) %%~ni
)
endlocal
The nested dir command ensures alphabetical ordering (reference.)
A remark why I put a pipe (|) as a delimiter: if you don't define a delimiter, the default space will be used. If your file name contains space then it would be truncated. So I picked a character that is not valid in file names ensuring the whole file name is returned.
Now if you get a number from the user by this:
set /p option=Choose your option:
after this command (evaluating and possibly re-requesting the input) to do a lookup for the file you can repeat BLOCK 1 but replace the echo line with examining the option like this:
if !counter! == %option%
and put those commands in the if block to do whatever you want to do with the file (for debugging, put back the echo command).

Loop through files in a folder and check if they have different extensions

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).

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
)

Batch file to perform a looped search based on the line items of a text file

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.

Resources