Loop to search files efficiently starting from last modified file - loops

I have a folder containing around 50 subfolders (called 0001, 0002, 0003... etc) and each contain hundreds of files (called 0001_132.txt, 0002_312.txt, 0032_152.txt... etc), and I need to run a batch file to extract files that I haven't processed since the last run of the batch file.
The way I currently do it is saving a .txt file telling me the filename of the last file being extracted during the last update. Next time I run the batch file, it loops through subfolders until reaching that particular filename and then start extracting the files newer than that particular file.
I am not sure if the way I do it is good or not but it works. HOWEVER, since I have many files in the folder, each time the batch file reads all old files before extracting new ones, which causes the program to run for quite some time. So, I would like to ask, if there is any way to not loop through those old files?
The file recording the last updated files looks something like this:
last_update.txt: format([subfoldername]_[file].txt)
eg: 0001_134
0002_213
.
.
0050_241
and my batch looks something like this:
for /f %%a in (C:\last_update.txt) do (
set number=%%a
set "subfolder=!number%:~0,4!"
set reach=0
for /f %%x in (c:\data\subfolder\) do (
if %%~nx==!number! (
set reach=1
)
if not %%~nx==!number! if !reach!==1 (
DO THE EXTRACTION
)
set latest_File=%%~nx
)
echo !latest!>>last_update_temp.txt
)
del last_update.txt
ren last_update_temp.txt last_update.txt

If you use the attrib command to clear the archive bit after processing your files, then you can use xcopy with /l to get a list of the new files with the archive bit set.
Here is an example - assuming the files have already been processed and new files are waiting with the archive bit set. The last line removes all the archive attributes once the files have been processed.
All it does is in the loop below is echo the drive:\folder\filename.txt of each file with the archive attribute set, and pause.
It uses a temporary file because of the number of files being processed, which could otherwise cause a long delay.
#echo off
dir *.txt /aa-d /b /s >file.tmp
for /f "delims=" %%a in (file.tmp) do (
rem do your task with all the files
echo "%%a"
pause
)
del file.tmp
attrib -a *.txt /s

Related

Batch file to rename files with complex names and back again

Reviving an old topic once discussed here, as I have a similar problem. The solution proposed in the old thread worked only in half in my case.
I need first to rename various media files (mp4, mp3, wav...) with irregular, sometimes complex names as 1.mp3, 2.mp4, 3.wav, etc. And some time after I need to restore the original filenames. File extensions should remain the same in both cases.
In a more specific case of renaming .raw files Helbreder proposed two .bat scripts. The first .bat changes filenames to 1.raw, 2.raw, 3.raw, etc. and creates corresponding individual .txt files each of which keeps the original filename. The second .bat takes the original filenames from individual .txt files and renames 1.raw, 2.raw, 3.raw, etc., back to the original.
For my purpose I slightly modified the first .bat proposed, and this works perfectly well:
#echo OFF
#setlocal ENABLEDELAYEDEXPANSION
set I=1
for %%G in (*.mp3 or *.3gp or *.wav or .mp4) do (
set ORIGINAL_NAME=%%~nG
set ORIGINAL_EXTENSION=%%~xG
(
REM Try to rename file
ren "%%G" "!I!"!ORIGINAL_EXTENSION!
) && (
REM Renaming was successful
> "!I!.txt" echo !ORIGINAL_NAME!!ORIGINAL_EXTENSION!
set /A I+=1
) || (
REM Renaming was a failure
echo Cannot rename [!ORIGINAL_NAME!] file.
)
)
#endlocal
Put in a destination directory, this .bat renames all media files, keeping the correct extensions, and generates a set of .txt files each of which contains the original filename with extension.
For instance, 1.txt contains a string "Play 2019-03-06 in C.mp3" which was the original filename.
Then I need to reverse the original filenames and I run the second unmodified Helbreder's .bat. For commodity purpose I paste it here:
#echo OFF
#setlocal ENABLEDELAYEDEXPANSION
for %%F in (*.txt) do (
set BASENAME=%%~nF
REM Read original name from txt file
for /F %%G in (%%F) do (
REM iterate over all corresponding files
for %%H in (!BASENAME!.*) do (
set EXTENSION=%%~xH
REM Remove dot from extension string
set EXTENSION=!EXTENSION:~1!
if not "!EXTENSION!" == "txt" (
REM Process files
(
REM try to rename corresponding file to old name
ren "!BASENAME!.!EXTENSION!" "%%G.!EXTENSION!"
) && (
REM Operation was successful - remove TXT file
del /F /Q "%%F"
) || (
REM Something went wrong
echo Cannot restore old name for file [!BASENAME!.!EXTENSION!].
)
)
)
)
)
#endlocal
As my filenames may be complex and often include blank spaces, this second .bat works in a half-successful way.
It reverted the first original filename "Play 2019-03-06 in C.mp3" written to a 1.txt with extension, as simply "Play.mp3". It also ignored a part of the second complex filename which followed blank space, keeping only "2007-03-06.mp3" instead of "2007-03-06 output.mp3". And it successfully reverted only those filenames which were composed of numbers and underscores, without blank spaces.
As far as I understand, the issue consists in the way the second .bat retrieves the filename from the text string kept in .txt file. The first blank space occurring in the text line is considered as the end of a filename.
Could you kindly suggest a solution for reverse renaming of any files from the correspondent .txt record, which may contain letters, numbers and blank spaces (and maybe special characters, like "&" and some others).
Following Compo's advise I tried another code from the old post, this time proposed by Jeb.
After a slight modification to make it match to different file types and - important! - to keep their original extensions, the code seems to work, but with issues.
When there is more than one dot in the filename the name revert code does not restore it completely, even though it is correctly saved in the .txt file by the rename batch. For example the second batch truncates "Play 2020.10.12.mp4" to "Play 2020" and does not wish to restore the extension.
Placed in directories with many files the rename batch sometimes does not rename a part of the list, sometimes does not do the job at all and sometimes renames all files flawlessly. I first thought the partial script dysfunction might be related to the presence of special signs in the filenames, like commas or parenthesis. But no, when I delete special signs, this changes nothing. Just puzzled.
So, the new version of code is only partially solving the problem. Is there something to correct in the code, to make it more universally working? Which solution could be applied in these two cases?
I divided the code into two separate .bat files to rename and to revert original filenames.
The both follow.
Rename:
#echo off
set cnt=0
del Names.txt > nul 2>&1
echo Running Batch Rename
for %%f in (*.mp3 or *.mp4 or *.3gp or *.wav) do (
set "file=%%f"
set "EXTENSION=%%~xf"
CALL :renameToNumber
)
:renameToNumber
set /A cnt+=1
set "number=00000%cnt%"
set "number=%number:~-4%"
(echo %number% %file%) >> Names.txt
ren "%file%" %number%"%EXTENSION%"
exit /b
Revert the original names:
for /F "tokens=1,*" %%A in (names.txt) DO (
set "number=%%A"
set "filename=%%~nB"
call ren %%number%%.* "%%filename%%.*"
call echo ren %%number%%.* "%%filename%%.*"
)
exit /b

Automate file replacement

I have a task to rename a file and place another file with same name in the same folder
Like for e.g. a folder C:/test I have multiple files .txt (suppose test.txt is the one needed rename and replace)
I want to rename test.txt to test_bkp%date% and place new file there.
Need help to start the logic.
#ECHO OFF
SET "sourcedir=C:\Users\test\new"
FOR /f "delims=" %%a IN (
'dir /b /a-d "%C:\Users\Test\new\%filenames%.txt" '
) DO (
SET "filename=%%a"
ECHO REN "%C:\Users\Test\new\%%a C:\Users\Test\new\%%a_bk_date.*%"
)
GOTO :EOF
Let me explain what I am trying to achieve. I get file with updated data often. I cannot just go ahead and replace the old file with new. I have to take a backup and place the new file in the folder.
This is my first try using batch scripting
As soon as you write a new version of a file into the same folder than the original (with the same name), it's already too late to rename the original - it doesn't exist anymore. You need two folders: one that receives new versions (new) and one where you keep your renamed files plus the new ones (old)
#ECHO OFF
setlocal
set "source=C:\Users\test\new"
set "destin=C:\Users\test\old"
set "files=*.txt"
REM for every matching file in the source folder:
for %%A in ("%source%\%files%") do (
REM if there is such a file in the destination folder, rename it:
if exist "%destin%\%%~nxA" ren "%destin%\%%~nxA" "%%~nA-%date%%%~xA"
REM only then copy the file:
copy "%%~fA" "%destin%\"
)
This will fail if you run it more than once per day. (More code needed to handle that; for example, adding time too)
It will also fail if your %date% contains characters that are not allowed in file names (my %date% looks like 29.01.2019). (More code needed to handle that; for example %date:/=-%)

How to check if zip or rar file contains 1 or more files?

For the purposes of saving space and organizing, I'm zipping bunch of files in my local and networked folders. They are mainly CAD files, like stp, igs, etc.
There are already existing zip files and some are extracted by other users, but the zip file still exists on the folders, which eats up space.
Is there a command line zip, rar, 7z. etc. to find out if an archive file contains only 1 file?
I'd like to figure this out as I'll extract the archives with single files in to the current directory whilst extracting archives with 1+ files to \archivename\ folder. Otherwise one folder with 30 STP files, will suddenly have 30 folders and 30 files extracted in them which I don't want.
I currently use a batch file with WinRAR to extract and another program to check for duplicates, then WinRAR batch to re-zip them based on file extension. (Reason: people use different archive methods and there are duplicates of files all over.)
Sample batch files:
for /F "delims=," %%f in ('dir *.stp /B' ) do (%path% a -afzip -r- -m5 -ed -ep -or -tl -y -df "%%f".zip "%%f")
for /F "delims=;" %%f in ('dir *.7z /B /S' ) do (%path% x -OR -ilogC:\Users\XXXX\Desktop\myLog.txt "%%f" "%%~dpf"\"%%~nf"\)
Once I can check for number of files in a zip, I'll add a recursive function.
I can use NTFS compression, but I also want to organize the folders, some folder have 1000 files in them, I surely want to reduce that to 1. These are mainly for archiving purposes.
Any help or thought would be appreciated.
I suggest the following commented batch file for this task:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem Extract all 7-Zip, RAR and ZIP archives in current directory into
rem subdirectories with name of archive file as name for subdirectory (-ad)
rem with running WinRAR for extraction in background (-ibck) which means
rem minimized to system tray with restoring also last access time (-tsa)
rem and creation time (-tsc) if existing in archive file and with skipping
rem files on extraction perhaps already present in the subdirectory with
rem same last modification time (-u), but overwriting automatically older
rem files in subdirectory if archive file contains an existing file with
rem a newer last modification time (-y) ignoring all errors (also -y).
for %%I in (7z rar zip) do "%ProgramFiles%\WinRAR\WinRAR.exe" x -ad -ibck -tsa -tsc -u -y *.%%I
rem If a subdirectory contains only 1 file, move that file to the current
rem directory with overwriting a perhaps already existing file with same
rem name in current directory and then remove the subdirectory.
for /D %%I in (*) do call :CheckSubDir "%%I"
rem Exit processing of the batch file without fall through to subroutine.
endlocal
goto :EOF
rem The subroutine CheckSubDir first checks for directories in directory
rem passed as parameter to the subroutine. A directory containing at
rem least one subdirectory is kept without any further processing.
rem If the directory does not contain a subdirectory, it searches for files
rem in the directory. If there are at least 2 files, the directory is kept
rem without any further processing.
rem But if the directory contains only 1 file, this file is moved to
rem current directory. Then the empty directory is deleted before exiting
rem the subroutine and continue batch file processing in calling loop.
rem Each directory containing no subdirectory and no file is removed, too.
:CheckSubDir
for /F "delims=" %%D in ('dir /AD /B "%~1\*" 2^>nul') do goto :EOF
setlocal EnableDelayedExpansion
set FileCount=0
for /F "delims=" %%F in ('dir /A-D /B "%~1\*" 2^>nul') do (
set /A FileCount+=1
if !FileCount! == 2 endlocal & goto :EOF
set "FileName=%%F"
)
if %FileCount% == 1 move /Y "%~1\%FileName%" "%FileName%"
rd "%~1"
endlocal
goto :EOF
Please read the comments for details what this batch file does on execution using WinRAR.
The batch file contains much more comment lines than real command lines.
2>nul in the last two FOR loops redirects the error message output by command DIR to handle STDERR in case of no directory or no file found to device NUL to suppress it. The redirection operator > must be escaped here with character caret ^ to be interpreted as redirection operator on execution of DIR command line and not already on parsing the FOR command line.
WinRAR supports many archive types on extraction. But WinRAR.exe is a GUI application and therefore does not support listing the contents of an archive file to console as Rar.exe supports.
The console version Rar.exe as well as free console application UnRAR.exe support both listing the archive file contents to handle STDOUT in various formats.
This difference on supported commands between WinRAR.exe and Rar.exe/UnRAR.exe can be seen by opening in WinRAR the help by clicking in menu Help on menu item Help topics, opening on help tab Contents the list item Command line mode, opening the list item Commands, clicking on list item Alphabetic commands list and comparing this list with the commands listed and described in text file Rar.txt in program files folder of WinRAR which is the manual for the console version.
Rar.txt lists and describes:
l[t[a],b] ... List archive contents [technical [all], bare]
v[t[a],b] ... Verbosely list archive contents [technical [all], bare].
Help of WinRAR does whether contain command l nor command v.
It would be of course also possible to run Rar.exe or UnRAR.exe on each *.rar file with command lb, count the number of lines output as done in above batch file to count the files and extract the *.rar archive file depending on the line count to current directory (1 line only) or to a subdirectory.
But it should be taken into account that on using bare list format and only 1 line output this line can be the name of an archived file or the name of an archived empty folder. The solution would be using standard list command and more analyze the attributes as well because a directory has attribute D while file does not have this attribute.
And for *.7z and *.zip files the same must be coded using 7z.exe or 7za.exe. The help of 7-Zip describes also the available commands and switches like the help of WinRAR.
But all those efforts do not make much sense in comparison to posted solution as the archive file has to be extracted at all and moving a file is done very fast as just the entry in file allocation table is modified and no data are copied or moved at all.
Running 7-Zip or Rar separately for first just listing each archive file contents, analyzing the list, and running 7-Zip or Rar once again on archive file for extraction is much slower than running WinRAR just 3 times (or less) to extract all archives and then move some files and remove some directories.
For understanding the used commands and how they work, open a command prompt window, execute there the following commands, and read entirely all help pages displayed for each command very carefully.
call /?
dir /?
echo /?
endlocal /?
for /?
goto /?
move /?
rd /?
set /?
setlocal /?
See also the Microsoft TechNet article Using command redirection operators.
Taking the question literal the following batch uses 7z.exe (has to be reachable via the Path) list (l)-option to get the number of files included in the archive by filtering the last line.
#Echo off
Set Base=q:\Test
Pushd "%Base%"
For /f "delims=" %%A in (
'Dir /B/S/A-D *.zip *.7z *.rar'
) Do For /f "tokens=5" %%B in (
' 7z.exe l "%%~A" ^| findstr "files$" '
) Do If %%B equ 1 (
Echo Archive %%A contains 1 file
) else (
Echo Archive %%A contains %%B files
)
Popd
Sample Output:
Archive q:\Test\archiv.7z contains 135 files
Archive q:\Test\PoSh\powershellitunes\PowerScript-itunes.7z contains 1 file
Archive q:\Test\PoSh\_pdf_itextsharp\extract_pdf_pages_into_new_323689.zip contains 3 files
Archive q:\Test\_StackOverflow\Noodles\Filter0.8.zip contains 4 files
Archive q:\Test\2016\12\16\Path.rar contains 7 files
Archive q:\Test\_AllHelp.Win\allhelp.zip contains 7 files
Archive q:\Test\2017-02\pkzipc_40.rar contains 10 files

Batch Script Move file to directory based on its current directory

I'm attempting to sort a lot of files based on the current location of the file e.g.:
File 1 is located at C:\Work\Movies\Subs\Subtitle.txt
File 2 is located at C:\Work\Movies\Subs\Special\Subtitle.txt
File 3 is located at C:\Work\MoviesSpanish\Subs\Subtitle.txt
I'm trying to move the files like so:
File 1 to C:\Work\InProgress\Movies\Subs\Subtitle.txt
File 2 to C:\Work\InProgress\Movies\Subs\Special\Subtitle.txt
File 3 to C:\Work\InProgress\MoviesSpanish\Subs\Subtitle.txt
The Batch Script is to be located in C:\Work\MoveFile.bat
There are away more files then I listed above. Just for an estimate I would say around 300-500 per folder and there's a lot more subdirectories (e.g. .\Subs\01\ all the way up to .\Subs\300\ and they each contain a bunch of text files). I need to move all of the .txt files from their current locations to a new folder in C:\Work\ while retaining the rest of the directory location. So they get moved to C:\Work\[New Folder]\[Rest of Original Location]
I want to do this in batch but I'm not sure where to start. I already have the following code, which deletes files that don't contain a specific string:
for /r %%Z in (*.txt) do (
SET "count="
for /F "usebackq delims=," %%A in ("%%Z") do (
if /i "%%A"=="LN" set count=1
)
if not defined count echo del "%%Z"
if not defined count del "%%Z"
if defined count move "%%Z"
echo %count%
echo %%Z
)
But I'm not sure how to obtain the correct directory to move them into. I was thinking about for loop that reads the directory string of the file in question and uses delims=/ but it kept reading the text file rather then the path (and when I didn't use quotes on %%Z, it decided it couldn't find the file).
This is untested - it uses robocopy to move the *.txt files into a new location which is outside the source folder (because of cyclic copy restrictions) and then moves them back to the new "C:\Work\InProgress" folder.
If %temp% and c:\work are on the same drive then it should be swift.
Change c:\work in both places to a test folder with a few copies of your files and test it.
#echo off
robocopy "C:\Work" "%temp%\temporarymoviefolder" *.txt /s /mov
robocopy "%temp%\temporarymoviefolder" "C:\Work\InProgress" *.txt /s /mov
pause

How to write a Batch Script that searches for files based on list

Can anyone assist me in writing a batch script that searches for file names and outputs the location of the files to a text file. Example I have a file called list.txt located in a folder, C:\LocateFiles\list.txt. Located in the list.txt file are about 25 file names that I wish to determine if they are anywhere on the C:\ drive. If it locates any of the file names identified in the file list.txt it will output the path of all files found to a single file in C:\LocatedFiles\results.txt.
A million thanks,
Johnny Mac
#ECHO OFF
FOR /F %%F IN (C:\LocateFiles\List.txt) DO DIR /s/p/b %%F.* >> C:\LocateFiles\finds.txt
Save that as LocateFiles.cmd and place it in whichever directory you wish to search, note that C:\ is very large and will take quite a while! as in, forever, seriously, i really wouldnt, your call...
the file finds.txt will have the entire path for any file that matches up to the file names listed in List.txt
Also note, this finds files of any extension, but the filename itself must match exactly to whats in List.txt
The solution below search the files in the current directory just once, so it run faster.
#echo off
dir /S /B /A-D | findstr /I /G:C:\LocateFiles\list.txt > C:\LocatedFiles\results.txt
EDIT: New method added
The method below may run even faster. It is necessary to complete a timing test.
#echo off
setlocal EnableDelayedExpansion
rem Read file names from file list and assemble a long string with this format:
rem "filename1.ext*" "filename2.ext*" ...
set "fileList="
for /F "delims=" %%a in (C:\LocateFiles\list.txt) do set fileList=!fileList! "%%a*"
rem Search the files from current directory downwards
(for /R %%a in (%fileList%) do echo %%a) > C:\LocatedFiles\results.txt

Resources