Batch list folder, count pdf files and get file name - windows

I would like to create a summary of all the files that I received weekly from other department. The folder structure shown below. Each folder will contain multiples pdf files and only 1 .docx file. So, i would like to list down the folder name, count total pdf files inside and get the name of docx file. If docx missing, show "missing", is this possible too?
I did try this code, but this will show all files in the folder instead.
#echo off
FOR /F "delims=" %%F IN ('dir /B /A /S *') DO (
for %%D in ("%%~dpF\.") do echo/ FolderName: %%~nxD ^| FileName: %%~nxF
)
pause
UPDATE:Today while Im testing suggested code, they send me last week file. AND to my surprised, the other department changed folder structure (facepalm!). Below updated folder structure for reference.
Updated folder structure:
Current
|count.bat
|--FolderA-Folder1-Nov 2020.docx
| -pdf1.pdf
| -pdf2.pdf
| -pdf3.pdf
| -pdf4.pdf
| Folder2-Dec 2020.docx
| -pdf1.pdf
| -pdf2.pdf
| -pdf3.pdf
| -pdf4.pdf
|--FolderB-Folder1-Nov 2020.docx
| -pdf1.pdf
| -pdf2.pdf
| -pdf3.pdf
| -pdf4.pdf
| Folder2-Dec 2020.docx
| -pdf1.pdf
| -pdf2.pdf
| -pdf3.pdf
| -pdf4.pdf
|--FolderC-Folder1-Nov 2020.docx
| -pdf1.pdf
| -pdf2.pdf
| -pdf3.pdf
| -pdf4.pdf
Expectation Results
FolderName: Folder A Folder 1 PDFtotal: 4pdfs DocFile: Nov 2020.docx
FolderName: Folder A Folder 2 PDFtotal: 4pdfs DocFile: Dec 2020.docx
FolderName: Folder B Folder 1 PDFtotal: 4pdfs DocFile: Nov 2020.docx
FolderName: Folder B Folder 2 PDFtotal: 4pdfs DocFile: Dec 2020.docx
FolderName: Folder C Folder 1 PDFtotal: 4pdfs DocFile: Nov 2020.docx

Original Script
The dir command without the /B option displays the count of matching items in the penultimate line, which could be captured and parsed by a for /F loop.
Since you have got a flat directory structure, I would not do a recursive approach (like dir /S or for /R).
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=." & rem // (target directory; `.` is current, `%~dp0.` is parent)
set "_MASK=Doc*.docx" & rem // (pattern to find particular file)
set _LIST="pdf" & rem // (space-separated list of file extensions without `.`)
rem // Loop through immediate sub-directories of the target directory:
for /D %%D in ("%_ROOT%\*") do (
rem // Return name of current sub-directory without trailing line-break:
< nul set /P ="FolderName: %%~nxD "
rem // Loop through given list of file extensions:
for %%E in (%_LIST%) do (
rem // Initialise variables:
set /A "CNT=0, NUM=0" & set "NAME="
rem // Capture number of matching files from last but one line of `dir`:
for /F "eol= " %%C in ('2^> nul dir /A:-D-H-S "%%~D\*.%%~E"') do (
2> nul set /A "CNT=NUM, NUM=%%C"
)
rem // Return count of matching files without trailing line-break:
< nul call set /P ="PDFtotal: %%CNT%%%%~Es "
)
rem // Find file that matches the given pattern (assuming there is only one):
for %%F in ("%%~D\%_MASK%") do set "NAME=%%~nxF"
rem // Return name of found file, with trailing line-break this time:
call echo DocFile: %%NAME%%
)
endlocal
exit /B
The option /A:-D-H-S of the dir command in the code prevents hidden and system files from being counted; change it to /A:-D if you do want to regard them as well.
Updated Script
In the following you will find the new code adapted to your changed directory structure (you will notice that there were only a few modifications necessary):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=." & rem // (target directory; `.` is current, `%~dp0.` is parent)
set "_MASK=* 2020.docx" & rem // (pattern to find particular file)
set _LIST="pdf" & rem // (space-separated list of file extensions without `.`)
rem // Loop through immediate sub-directories of the target directory:
for /D %%S in ("%_ROOT%\*") do (
rem // Loop through next level of sub-directories:
for /D %%D in ("%%~S\*") do (
rem // Return names of current sub-directories without trailing line-break:
< nul set /P ="FolderName: %%~nxS %%~nxD "
rem // Loop through given list of file extensions:
for %%E in (%_LIST%) do (
rem // Initialise variables:
set /A "CNT=0, NUM=0" & set "NAME="
rem // Capture number of matching files from last but one line of `dir`:
for /F "eol= " %%C in ('2^> nul dir /A:-D-H-S "%%~D\*.%%~E"') do (
2> nul set /A "CNT=NUM, NUM=%%C"
)
rem // Return count of matching files without trailing line-break:
< nul call set /P ="PDFtotal: %%CNT%%%%~Es "
)
rem // Find file that matches the given pattern (assuming there is only one):
for %%F in ("%%~D\%_MASK%") do set "NAME=%%~nxF"
rem // Return name of found file, with trailing line-break this time:
call echo DocFile: %%NAME%%
)
)
endlocal
exit /B

#echo off
setlocal enabledelayedexpansion
set "spaces= "
FOR /F "delims=" %%F IN ('dir /B /AD /S *') DO (
rem new foldername in %%F
set "foldername=%%~nxF%spaces%"
set /a pdfcount=0
set /a docxcount=0
set "docxmessage=missing"
for /f "delims=" %%D in ('dir /B /A-D "%%F\*"') do (
if /i "%%~xD"==".pdf" set /a pdfcount+=1
if /i "%%~xD"==".docx" set /a docxcount+=1&set "docxmessage=%%~nxD"
)
set "pdfcount=%spaces%!pdfcount!"
if !docxcount! gtr 1 (set "docxcount= MULTIPLE") else (set "docxcount=")
echo/ FolderName: !foldername:~0,20! PDFtotal: !pdfcount:~-3!pdfs Docfile: !docxmessage! !docxcount!
)
Note that the first dir command now contains AD to list directories only.
With each directoryname, record it in foldername and add some spaces at the end. Reset the counters and establish the missing value in the docxmessage.
Then get the filename list within the directory found, count each .pdf and .docx and if any .docx is found, record its name in docxmessage
Modify docxcount to a message if more than 1 .docx is present, and empty otherwise
When the directory-scan is finished, apply some spaces to the start of pdfcount and then produce your output line using the current values of the variables and using the modifiers to produce the first 20 characters of the padded directoryname and the last 3 characters of the padded pdfcount.
When you use the point-click-and-giggle method of executing a batch, the batch window will often close if a syntax-error is found. You should instead open a 'command prompt' and run your batch from there so that the window remains open and any error message will be displayed.
Fixed : for /F %%D... needed "delims=" to assign the whole filename to %%D
Revised code for new structure:
#echo OFF
setlocal ENABLEDELAYEDEXPANSION
PUSHD U:\sourcedir
set "spaces= "
FOR /F "delims=" %%F IN ('dir /B /AD /S *') DO (
rem new foldername in %%F
set "foldername=%%~nxF%spaces%"
rem find parent directory
FOR /F "delims=" %%Q IN ("%%~dpF\.") DO SET "parent=%%~nxQ%spaces%"
rem is a leaf directory if this directory has no subdirectories
SET "isleaf=Y"
FOR /F "delims=" %%Q IN ('dir /B /AD "%%F"') DO SET "isleaf="
set /a pdfcount=0
set /a docxcount=0
set "docxmessage=missing"
for /f "delims=" %%D in ('dir /B /A-D "%%F\*" 2^>nul') do (
if /i "%%~xD"==".pdf" set /a pdfcount+=1
if /i "%%~xD"==".docx" set /a docxcount+=1&set "docxmessage=%%~nxD"
)
set "pdfcount=%spaces%!pdfcount!"
if !docxcount! gtr 1 (set "docxcount= MULTIPLE") else (set "docxcount=")
IF DEFINED isleaf echo/ FolderName: !parent:~0,20! !foldername:~0,20! PDFtotal: !pdfcount:~-3!pdfs Docfile: !docxmessage! !docxcount!
)
popd
GOTO :EOF
Some small additions to the code.
first, find the parent directoryname. By using the directoryname in %%F and adding \., the resultant resolved string appears to be a filename, the name and extension of which is the parent directory; store this in parent with padding.
Next, determine whether this is a leaf directory. Set the flag isleaf to assume it is a leaf directory, then perform a dir/ad so that any detected subdirectory name will clear the flag.
Then just process the directory as before, using 2^>nul to suppress file not found messages if the directory %%F contains no files.
...And only generate the report line if isleaf is defined, with the parent directoryname added.

This can be placed into a .bat file script and run in cmd.exe. Note that this does not identify directories which contain no .pdf file, no .docx file, or multiple .docx files.
powershell.exe -NoLogo -NoProfile -Command ^
"$Dirs = Get-ChildItem -Directory -Path 'C:\src\t';" ^
"foreach ($Dir in $Dirs) {" ^
" $PdfCount = (Get-ChildItem -File -Path $Dir.fullname -Filter '*.pdf').Count;" ^
" if (($null -ne $PdfCount) -and (0 -ne $PdfCount)) {" ^
" $DocxFiles = (Get-ChildItem -File -Path $Dir -Filter '*.docx');" ^
" if ($null -ne $DocxFiles) { $DocxFile = $DocxFiles[0] } else { $DocxFile = 'none' };" ^
" """FolderName: $($Dir.Name) PDFtotal: $($PdfCount.ToString())pdfs DocFile: $DocxFile""";" ^
" }" ^
"}"
It would be easier to put the script into a file.
=== Get-DocxAndPdfCount.ps1
[CmdletBinding()]
Param()
$Dirs = Get-ChildItem -Directory
foreach ($Dir in $Dirs) {
Write-Verbose "dir is $($Dir.fullname)"
$PdfCount = (Get-ChildItem -File -Path $Dir.fullname -Filter '*.pdf').Count
Write-Verbose $PdfCount
if ($null -ne $PdfCount -and 0 -ne $pdfCount) {
Write-Verbose $PdfCount
$DocxFiles = (Get-ChildItem -File -Path $Dir -Filter '*.docx')
if ($null -ne $DocxFiles) { $DocxFile = $DocxFiles[0] } else { $DocxFile = 'none' }
"FolderName: $($Dir.Name) PDFtotal: $($PdfCount.ToString())pdfs DocFile: $DocxFile"
}
}
Then, run it in a PowerShell console using:
.\Get-DocxAndPdfCount
.\Get-DocxAndPdfCount -Verbose # to see debugging information
Run from cmd.exe using:
powershell -NoLogo -NoProfile -File '.\Get-DocxAndPdfCount'
powershell -NoLogo -NoProfile -File ".\Get-DocxAndPdfCount" -Verbose

Related

Issues %% and/or !! variables in Batch script

I'm trying to write a batch file that will extract a zip file. (This part works) It should read the restoretranslation.conf file, which consists of two comma separated values. The first value is the file name, and the second is the file path where the respective file would reside, (if existing).
If value one exists in the extracted zip directory and is defined with a path in the conf file, it should prompt the user if they want to replace the file. If yes, backup the file in the path in the second value. (%%b in the conf file) to %%b\original_%%a. we should then replace the file.
It seems like most of my variables are not being parsed correctly. Could someone give me some pointers?
Here's an example of the data in the configuration file:
license.xml,C:\Program Files (x86)\programname
Script:
#echo on
rem Set the directory where the ZIP files are located
set zipfolder=%~dp0
setlocal enabledelayedexpansion
rem Get a list of all ZIP files in the directory that start with "RTSBACKUP-"
set cnt=0
for /f "delims=" %%a in ('dir /b /a-d "%zipfolder%RTSBACKUP-*.zip"') do (
set /A cnt+=1
set "zipfile=%%a"
set "zipfile=!zipfile:~0!"
echo !cnt! - !zipfile!
)
rem Prompt the user to select a file to extract
set /p "fileNum=Enter the number of the file you want to extract: "
rem Extract the selected file
set cnt=0
for /f "delims=" %%b in ('dir /b /a-d "%zipfolder%RTSBACKUP-*.zip"') do (
set /A cnt+=1
if !cnt!==%fileNum% (
set "zipfile=%%b"
echo %zipfile%
pause
set "zipfile=!zipfile:~0!"
start /min "***Compressing backup files...*** ***Do not close this window.***" /wait powershell Expand-Archive -LiteralPath "%zipfolder%%zipfile%" -DestinationPath "%zipfolder%%zipfile:~0,-4%"
echo The file %zipfile% has been extracted successfully!
goto :break
)
)
endlocal disabledelayedexpansion
:break
set extractedfolder=%zipfolder%!zipfile:~0,-4!\!zipfile:~0,-4!\
rem Set the location of the restoretranslation.conf file
set "resttranslationfile=%~dp0restoretranslation.conf"
rem Loop through all files in the extractedfolder
for /f "delims=" %%i in ('dir /b /a-d "%extractedfolder%*.*"') do (
set "file=%%~i"
rem Loop through the resttranslation.conf file
for /f "tokens=1,2 delims=," %%a in ("%resttranslationfile%") do (
if /i "!FILE!"=="%%a" (
set "dirto=%%b"
rem Ask for confirmation before restoring the file
set /p confirm=Do you want to restore %file% to %dirto%? (^Y/N^)^:
if /i "%confirm%"=="y" (
rem Backup the original file
echo Backing up original file to !extractedfolder!original_%file%
if exist "%dirto%\%file%" (
copy "%dirto%\%file%" "!extractedfolder!original_%file%"
)
rem Copy the backup file to the specified destination
echo Restoring !file! to %%b
copy "!extractedfolder!!file!" "%%b\"
)
)
)
)
pause
Output:
C:\Tyler>rem Set the directory where the ZIP files are located
C:\Tyler>set zipfolder=C:\Tyler\
C:\Tyler>setlocal enabledelayedexpansion
C:\Tyler>rem Get a list of all ZIP files in the directory that start
with "RTSBACKUP-"
C:\Tyler>set cnt=0
C:\Tyler>for /F "delims=" %a in ('dir /b /a-d
"C:\Tyler\RTSBACKUP-*.zip"') do ( set /A cnt+=1 set "zipfile=%a" set
"zipfile=!zipfile:~0!" echo !cnt! - !zipfile! )
C:\Tyler>( set /A cnt+=1 set
"zipfile=RTSBackup-RSC-ERIELAB2-01202023-171815.zip" set
"zipfile=!zipfile:~0!" echo !cnt! - !zipfile! ) 1 -
RTSBackup-RSC-ERIELAB2-01202023-171815.zip
C:\Tyler>rem Prompt the user to select a file to extract
C:\Tyler>set /p "fileNum=Enter the number of the file you want to
extract: " Enter the number of the file you want to extract: 1
C:\Tyler>rem Extract the selected file
C:\Tyler>set cnt=0
C:\Tyler>for /F "delims=" %b in ('dir /b /a-d
"C:\Tyler\RTSBACKUP-*.zip"') do ( set /A cnt+=1 if !cnt! == 1 ( set
"zipfile=%b" echo RTSBackup-RSC-ERIELAB2-01202023-171815.zip pause
set "zipfile=!zipfile:~0!" start /min "***Compressing backup
files...*** ***Do not close this window.***" /wait powershell
Expand-Archive -LiteralPath
"C:\Tyler\RTSBackup-RSC-ERIELAB2-01202023-171815.zip" -DestinationPath
"C:\Tyler\RTSBackup-RSC-ERIELAB2-01202023-171815" echo The file
RTSBackup-RSC-ERIELAB2-01202023-171815.zip has been extracted
successfully! goto :break ) )
C:\Tyler>( set /A cnt+=1 if !cnt! == 1 ( set
"zipfile=RTSBackup-RSC-ERIELAB2-01202023-171815.zip" echo
RTSBackup-RSC-ERIELAB2-01202023-171815.zip pause set
"zipfile=!zipfile:~0!" start /min "***Compressing backup files...***
***Do not close this window.***" /wait powershell Expand-Archive -LiteralPath "C:\Tyler\RTSBackup-RSC-ERIELAB2-01202023-171815.zip" -DestinationPath "C:\Tyler\RTSBackup-RSC-ERIELAB2-01202023-171815" echo The file RTSBackup-RSC-ERIELAB2-01202023-171815.zip has been
extracted successfully! goto :break ) )
RTSBackup-RSC-ERIELAB2-01202023-171815.zip Press any key to continue .
. . The file RTSBackup-RSC-ERIELAB2-01202023-171815.zip has been
extracted successfully
C:\Tyler>set extractedfolder=C:\Tyler\!zipfile:~0,-4!\!zipfile:~0,-4!\
C:\Tyler>rem Set the location of the restoretranslation.conf file
C:\Tyler>set "resttranslationfile=C:\Tyler\restoretranslation.conf"
C:\Tyler>rem Loop through all files in the extractedfolder
C:\Tyler>for /F "delims=" %i in ('dir /b /a-d
"C:\Tyler\RTSBackup-RSC-ERIELAB2-01202023-171815\RTSBackup-RSC-ERIELAB2-01202023-171815\*.*"')
do ( set "file=%~i" rem Loop through the resttranslation.conf file
for /F "tokens=1,2 delims=," %a in
("C:\Tyler\restoretranslation.conf") do (if /I "!FILE!" == "%a" ( set
"dirto=%b" rem Ask for confirmation before restoring the file set /p
confirm=Do you want to restore to ? (Y/N): if /I "" == "y" ( rem
Backup the original file echo Backing up original file to
!extractedfolder!original_ if exist "\" (copy "\"
"!extractedfolder!original_" ) rem Copy the backup file to the
specified destination echo Restoring !file! to %b copy
"!extractedfolder!!file!" "%b\" ) ) ) )
C:\Tyler>( set "file=License.xml" rem Loop through the
resttranslation.conf file for /F "tokens=1,2 delims=," %a in
("C:\Tyler\restoretranslation.conf") do (if /I "!FILE!" == "%a" ( set
"dirto=%b" rem Ask for confirmation before restoring the file set /p
confirm=Do you want to restore to ? (Y/N): if /I "" == "y" ( rem
Backup the original file echo Backing up original file to
!extractedfolder!original_ if exist "\" (copy "\"
"!extractedfolder!original_" ) rem Copy the backup file to the
specified destination echo Restoring !file! to %b copy
"!extractedfolder!!file!" "%b\" ) ) ) )
C:\Tyler>(if /I "!FILE!" == "C:\Tyler\restoretranslation.conf" ( set
"dirto=" rem Ask for confirmation before restoring the file set /p
confirm=Do you want to restore to ? (Y/N): if /I "" == "y" ( rem
Backup the original file echo Backing up original file to
!extractedfolder!original_ if exist "\" (copy "\"
"!extractedfolder!original_" ) rem Copy the backup file to the
specified destination echo Restoring !file! to copy
"!extractedfolder!!file!" "\" ) ) )

Delete a folder if a sub-sub-folder within it has files older than 30 days

I have the following path : C:\folder\subfolder1\subfolder2
I would like to check if all files within "subfolder2" are over 30 days old and if so delete C:\folder path (not only subfolder2).
Using forfiles I can delete subfolder2 but not delete up to parent folder (C:\folder) :
#echo off
forfiles.exe /s /d -30 /p "C:\folder" /c "cmd.exe /c IF #isdir == TRUE rd /s /q #path"
exit
Do you have any idea how to do this ?
Thank you for your assistance.
Do not use the /S option but let forfiles only process the sub-sub-folder, then delete the top-level folder in case all found files are at least 30 days old (meaning they have been last modified 30 days ago or earlier):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=U:\TEST\top" & rem // (full path to top-level directory)
set "_LEV1=*" & rem // (name or mask of sub-directory)
set "_LEV2=*" & rem // (name or mask of sub-sub-directory)
set "_MASK=*" & rem // (mask for files in sub-sub-directory)
set /A "_AGE=30" & rem // (age in number of days of the files)
rem // Initialise counter for files that are newer than the given age:
set /A "SUM=0"
rem // Iterate through all matching sub-directories of the top-level directory:
for /D %%A in ("%_ROOT%\%_LEV1%") do (
rem // Iterate through all matching sub-sub-directories of the current sub-directory:
for /D %%B in ("%%~A\%_LEV2%") do (
rem // Initialise an auxiliary flag that indicates whether the following loop iterates the second time:
set "FLAG="
rem /* In the following `for /F` loop two `forfiles` command lines are executed in order to retrieve the
rem number of matching files in the current sub-sub-directory that are newer than the given age;
rem since `forfiles` cannot return files newer than a given age (since `/D +` points to the future!),
rem let us determine the diffenence between the numbers of all files and such older than the given age;
rem the condition `if #isdir==FALSE` is intentionally omitted in the first `forfiles` command line,
rem because otherwise, sub-directories od the current sub-sub-directory would then become lost: */
for /F %%C in ('
rem/ /* Determine numbers of all files minus aged out files: */ ^& ^
2^> nul forfiles /P "%%~B" /M "%_MASK%" /C "cmd /C echo #" ^| find /C "#" ^& ^
2^> nul forfiles /P "%%~B" /M "%_MASK%" /D -%_AGE% /C "cmd /C if #isdir==FALSE echo #" ^| find /C "#"
') do (
rem /* The flag is not defined in the first iteration when all files are counted, but is is defined in
rem the second iteration when newer files are filtered out and the older ones are counted: */
if defined FLAG (set /A "SUM-=%%C") else set /A "SUM+=%%C" & set "FLAG=#"
)
)
rem // The following line is merely intended for debugging purposes and may be removed:
>&2 (for /F %%D in ('set /A "SUM"') do echo Processing "%%~nxA\%%~nxB": found %%D newer file^(s^) so far...)
)
rem // The following line does actually not delete anything unless you remove the upper-case `ECHO` statement:
if %SUM% equ 0 ECHO rd /S /Q "%_ROOT%"
endlocal
exit /B

Move files into a subfolder of a destination directory

I started using batch commands last week and I've reached a real obstacle with a script I made.
What I want to do
Move a PDF file from C:\Users\JK\Documents\reports PDFs into pre-made subfolders in the destination W:\Departments\QA\cases.
For example the script would move 2223 report.pdf to W:\Departments\QA\cases\2201 - 2300\2223
What I tried
I made a script based off the answer in this thread
cls
#pushd %~dp0
#echo off
setlocal EnableExtensions DisableDelayedExpansion
set "SourceDir=C:\Users\JK\Documents\reports PDFs"
set "DestDir=W:\Departments\QA\cases\"
for /F "eol=| delims=" %%A in ('dir /B /A-D-H "%SourceDir%\*.pdf" 2^>nul') do (
for /F "eol=| tokens=1" %%B in ("%%~nA") do (
for /D %%C in ("%DestDir%\%%B*") do move /Y "%SourceDir%\%%A" "%%C\"
)
)
endlocal
popd
pause
Where I am stuck
How could I add subfolders or account for them in the destination directory?
FYI, I also tried adding a wildcard symbol at the end of the destination directory by changing %DestDir%\%%B to %DestDir%\*\%%B*.
I would probably accomplish the task with the following script (see all the explanatory rem remarks):
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_SOURCE=C:\Users\JK\Documents\reports PDFs" & rem // (source directory; `.` or `` is current, `%~dp0.` is script parent)
set "_TARGET=W:\Departments\QA\cases" & rem // (target directory; `.` or `` is current, `%~dp0.` is script parent)
set "_SUBDIR=#" & rem // (set to non-blank value in order to create individual sub-directories)
set "_FMASK=????*.pdf" & rem // (pattern to match source files)
set "_DMASK=???? - ????*" & rem // (pattern to match target directories)
set "_FFIL1=^[0123456789][0123456789][0123456789][0123456789] .*\.pdf$" & rem // (filter 1 for source files)
set "_FFIL2=^[0123456789][0123456789][0123456789][0123456789]\.pdf$" & rem // (filter 2 for source files)
set "_DFIL1=^[0123456789][0123456789][0123456789][0123456789] - [0123456789][0123456789][0123456789][0123456789] .*$"
set "_DFIL2=^[0123456789][0123456789][0123456789][0123456789] - [0123456789][0123456789][0123456789][0123456789]$"
rem // Change into source directory:
pushd "%_SOURCE%" && (
rem // Iterate through all files matching the specified pattern and filters:
for /F "eol=| delims=" %%F in ('
dir /B /A:-D-H-S "%_FMASK%" ^| findstr /I /R /C:"%_FFIL1%" /C:"%_FFIL2%"
') do (
rem // Store full path of currently iterated file:
set "FILE=%%~fF"
rem // Extract the leading numeric part of the file name:
for /F "eol=| delims= " %%N in ("%%~nF") do (
rem // Store the numeric part:
set "NUM=%%N"
rem /* Remove any leading zeros from the numeric part of the file name, because
rem such cause the number to be unintentionally interpreted as octal: */
set /A "INT=0" & for /F "eol=| tokens=* delims=0" %%Z in ("%%N") do 2> nul set /A "INT=%%Z"
)
rem // Change into target directory:
pushd "%_TARGET%" && (
rem // Iterate through all directories matching the specified pattern and filters:
for /F "eol=| delims=" %%D in ('
cmd /V /C 2^> nul dir /B /A:D-H-S "!_DMASK:|=%%I!" ^| findstr /I /R /C:"%_DFIL1%" /C:"%_DFIL2%"
') do (
rem // Store name of currently iterated directory:
set "TDIR=%%D"
rem // Extract first (from) and second (to) numeric parts of directory names:
for /F "eol=| tokens=1-2 delims=- " %%S in ("%%D") do (
rem // Remove any leading zeros from first (from) numeric part:
set /A "FRM=0" & for /F "eol=| tokens=* delims=0" %%Z in ("%%S") do set /A "FRM=%%Z"
rem // Remove any leading zeros from second (to) numeric part:
set /A "TOO=0" & for /F "eol=| tokens=* delims=0" %%Z in ("%%T") do set /A "TOO=%%Z"
)
rem // Toggle delayed variable expansion to avoid trouble with `!`:
setlocal EnableDelayedExpansion
rem /* Do integer comparisons to check whether the numeric part of the file name
rem lies within the range given by the directory name (from/to): */
if !INT! geq !FRM! if !INT! leq !TOO! (
rem // Numeric part of file name lies within range, hence try to move file:
if defined _SUBDIR (
2> nul md "!TDIR!\!NUM!"
ECHO move "!FILE!" "!TDIR!\!NUM!\"
) else (
ECHO move "!FILE!" "!TDIR!\"
)
)
endlocal
)
rem // Return from target directory:
popd
)
)
rem // Return from source directory:
popd
)
endlocal
exit /B
You may need to adapt a few constants to your situation in the section commented with Define constants here: at the top.
After having tested the script for the correct output, remove the upper-case ECHO commands in front of the move commands! In order to avoid multiple 1 file(s) moved. messages, replace these ECHO commands by > nul.

How to search specific files from two folders and compare timestamp if the same or what is the newest files given using Batch Windows Scripting

I have initial code for searching and comparing in ALL files:
SET FILE1=Directory1\*
SET FILE2=Directory2\*
FOR %%i IN (%FILE1%) DO SET DATE1=%%~ti
FOR %%i IN (%FILE2%) DO SET DATE2=%%~ti
IF "%DATE1%"=="%DATE2%" ECHO Files have same age && GOTO END
FOR /F %%i IN ('DIR /B /O:D %FILE1% %FILE2%') DO SET NEWEST=%%i
ECHO Newer file is %NEWEST%
:END
How do specifically search based on the directory that this certain file name should be search and compare?
Sample:
I want to search all specific like INV and SLS file in its name and compare if the same or what is the newest file:
in Directory1:
Flatfile Timestamp
------- ----------
INVF032 3/14/2019
INVF021 3/14/2019
KVNF234 3/14/2019
SLS0234 3/14/2019
SLS3211 3/14/2019
in Directory2:
Flatfile Timestamp
------- ----------
INVF032 3/19/2019
INVF021 3/19/2019
KVNF234 3/19/2019
SLS0234 3/19/2019
SLS3211 3/19/2019
Output:
Newer files is:
INVF032 3/19/2019
INVF021 3/19/2019
SLS0234 3/19/2019
SLS3211 3/19/2019
Adding some echo on errors in the output after running in Windows Server 2008(It says instance not available):
C:\Users\ksaycon\Desktop>sample.bat
C:\Users\ksaycon\Desktop>rem // Author: KJSaycon: Batch Script Code for JDE Flat
file Extraction version 1
C:\Users\ksaycon\Desktop>setlocal EnableExtensions DisableDelayedExpansion
C:\Users\ksaycon\Desktop>echo "Program is running. ."
"Program is running. ."
C:\Users\ksaycon\Desktop>rem // Defining the constant variables and pointers:
C:\Users\ksaycon\Desktop>set "_ROOT=C:\Users\ksaycon\Desktop\." & rem // (set
root directory; `C:\Users\ksaycon\Desktop\.` means is script's parent directory,
`.` means current PATH)
C:\Users\ksaycon\Desktop>set "_DIR1=C:\Users\ksaycon\Desktop\.\fp_files1\" & r
em // (1st directory containing files)
C:\Users\ksaycon\Desktop>set "_DIR2=C:\Users\ksaycon\Desktop\.\fp_files2\" & r
em // (2nd directory containing files)
C:\Users\ksaycon\Desktop>set _MASKS="*INV*" "*SLS*" "*PUR*" & rem // (list
of quoted file masks, find string like *char*)
C:\Users\ksaycon\Desktop>set "_TMP1=C:\Users\ksaycon\AppData\Local\Temp\179\samp
le_1_27853.tmp" & rem // (set temp file 1)
C:\Users\ksaycon\Desktop>set "_TMP2=C:\Users\ksaycon\AppData\Local\Temp\179\samp
le_2_15411.tmp" & rem // (set temp file 2)
C:\Users\ksaycon\Desktop>rem // Loop 1st directory to be an absolute path:
C:\Users\ksaycon\Desktop>for %E in ("C:\Users\ksaycon\Desktop\.\fp_files1\") do
set "RDIR1=%~fE"
C:\Users\ksaycon\Desktop>set "RDIR1=C:\Users\ksaycon\Desktop\fp_files1\"
C:\Users\ksaycon\Desktop>rem // Loop 2nd directory to be an absolute path:
C:\Users\ksaycon\Desktop>for %E in ("C:\Users\ksaycon\Desktop\.\fp_files2\") do
set "RDIR2=%~fE"
C:\Users\ksaycon\Desktop>set "RDIR2=C:\Users\ksaycon\Desktop\fp_files2\"
C:\Users\ksaycon\Desktop>rem // Write all matching files in 1st directory to 1st
temporary file using assembly language function PUSHD:
C:\Users\ksaycon\Desktop>pushd "C:\Users\ksaycon\Desktop\fp_files1\" && (
for %F in ("*INV*" "*SLS*" "*PUR*") do (echo(%~nxF )
popd
) 1>"C:\Users\ksaycon\AppData\Local\Temp\179\sample_1_27853.tmp"
C:\Users\ksaycon\Desktop>rem // Write all matching files in 2nd directory to 2nd
temporary file using assembly language function PUSHD:
C:\Users\ksaycon\Desktop>pushd "C:\Users\ksaycon\Desktop\fp_files2\" && (
for %F in ("*INV*" "*SLS*" "*PUR*") do (echo(%~nxF )
popd
) 1>"C:\Users\ksaycon\AppData\Local\Temp\179\sample_2_15411.tmp"
C:\Users\ksaycon\Desktop>rem // Loop over all common files from both temporary f
iles:
C:\Users\ksaycon\Desktop>for /F %L in ('findstr /L /I /X /G:"C:\Users\ksaycon\Ap
pData\Local\Temp\179\sample_1_27853.tmp" "C:\Users\ksaycon\AppData\Local\Temp\17
9\sample_2_15411.tmp"') do (
rem // Build absolute `wmic`-compatible file paths:
set "FILE1=C:\\Users\\ksaycon\\Desktop\\fp_files1\\\\%L" & set "FILE2=C:\\Use
rs\\ksaycon\\Desktop\\fp_files2\\\\%L"
setlocal EnableDelayedExpansion
rem set "FILE1=&=&!" & set "FILE2=&=&!"
rem // Loop to get standardised file date/time (last modification) of 1st file
by `wmic`:
for /F "skip=1" %K in ('wmic DataFile where "Name='!FILE1!'" get LastModified')
do for /F %K in ("%K") do set "DATE1=%K"
rem // Loop to get standardised file date/time (last modification) of 2nd file
by `wmic`:
for /F "skip=1" %K in ('wmic DataFile where "Name='!FILE2!'" get LastModified')
do for /F %K in ("%K") do set "DATE2=%K"
rem // If Compare file dates/times (last mod.) of both files and return differi
ng ones:
if !DATE1! LSS !DATE2! echo "!FILE1:\\=\!" is older than "!FILE2:\\=\!"
if !DATE1! GTR !DATE2! echo "!FILE1:\\=\!" is newer than "!FILE2:\\=\!"
if !DATE1! EQU !DATE2! echo "!FILE1:\\=\!" and "!FILE2:\\=\!" are of same age
endlocal
)
C:\Users\ksaycon\Desktop>(
rem // Build absolute `wmic`-compatible file paths:
set "FILE1=C:\\Users\\ksaycon\\Desktop\\fp_files1\\\\FFPDFINV01" & set "FILE2
=C:\\Users\\ksaycon\\Desktop\\fp_files2\\\\FFPDFINV01"
setlocal EnableDelayedExpansion
rem set "FILE1=&=&!" & set "FILE2=&=&!"
rem // Loop to get standardised file date/time (last modification) of 1st file
by `wmic`:
for /F "skip=1" %K in ('wmic DataFile where "Name='!FILE1!'" get LastModified')
do for /F %K in ("%K") do set "DATE1=%K"
rem // Loop to get standardised file date/time (last modification) of 2nd file
by `wmic`:
for /F "skip=1" %K in ('wmic DataFile where "Name='!FILE2!'" get LastModified')
do for /F %K in ("%K") do set "DATE2=%K"
rem // If Compare file dates/times (last mod.) of both files and return differi
ng ones:
if !DATE1! LSS !DATE2! echo "!FILE1:\\=\!" is older than "!FILE2:\\=\!"
if !DATE1! GTR !DATE2! echo "!FILE1:\\=\!" is newer than "!FILE2:\\=\!"
if !DATE1! EQU !DATE2! echo "!FILE1:\\=\!" and "!FILE2:\\=\!" are of same age
endlocal
)
No Instance(s) Available.
" do set "DATE1=Desktop>for /F %K in ("
No Instance(s) Available.
" do set "DATE2=Desktop>for /F %K in ("
"C:\Users\ksaycon\Desktop\fp_files1\\FFPDFINV01" and "C:\Users\ksaycon\Desktop\f
p_files2\\FFPDFINV01" are of same age
C:\Users\ksaycon\Desktop>rem // Clean up temporary files:
C:\Users\ksaycon\Desktop>del "C:\Users\ksaycon\AppData\Local\Temp\179\sample_1_2
7853.tmp" "C:\Users\ksaycon\AppData\Local\Temp\179\sample_2_15411.tmp"
C:\Users\ksaycon\Desktop>echo "Clearing up garbage collection! Done."
"Clearing up garbage collection! Done."
C:\Users\ksaycon\Desktop>endlocal
C:\Users\ksaycon\Desktop>exit /B
C:\Users\ksaycon\Desktop>
Your intention is not quite clear, particularly because your desired output is ambiguous as it does not contain the parent directories of the files.
Anyway, here is a script that does what you want (at least for my understanding), given that only files that are present in both directories are to be processed.
So this is the code, containing several explanatory rem-remarks:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem // Define constants here:
set "_ROOT=%~dp0." & rem // (common root directory; `%~dp0.` is script's parent, `.` is current)
set "_DIR1=%_ROOT%\Directory1" & rem // (1st directory containing files)
set "_DIR2=%_ROOT%\Directory2" & rem // (2nd directory containing files)
set _MASKS="INV*." "SLS*." & rem // (list of quoted file masks)
set "_TMP1=%TEMP%\%~n0_1_%RANDOM%.tmp" & rem // (1st temporary file)
set "_TMP2=%TEMP%\%~n0_2_%RANDOM%.tmp" & rem // (2nd temporary file)
rem // Resolve 1st directory to be an absolute path:
for %%E in ("%_DIR1%") do set "RDIR1=%%~fE"
rem // Resolve 2nd directory to be an absolute path:
for %%E in ("%_DIR2%") do set "RDIR2=%%~fE"
rem // Write all matching files in 1st directory to 1st temporary file:
pushd "%RDIR1%" && (
for %%F in (%_MASKS%) do (
echo(%%~nxF
)
popd
) > "%_TMP1%"
rem // Write all matching files in 2nd directory to 2nd temporary file:
pushd "%RDIR2%" && (
for %%F in (%_MASKS%) do (
echo(%%~nxF
)
popd
) > "%_TMP2%"
rem // Loop over all common files from both temporary files:
for /F %%L in ('findstr /L /I /X /G:"%_TMP1%" "%_TMP2%"') do (
rem // Build absolute `wmic`-compatible file paths:
set "FILE1=%RDIR1:\=\\%\\%%L" & set "FILE2=%RDIR2:\=\\%\\%%L"
setlocal EnableDelayedExpansion
rem set "FILE1=%!FILE1:&=&!" & set "FILE2=%!FILE2:&=&!"
rem // Get standardised file date/time (last modification) of 1st file by `wmic`:
for /F "skip=1" %%K in ('wmic DataFile where "Name='!FILE1!'" get LastModified') do for /F %%K in ("%%K") do set "DATE1=%%K"
rem // Get standardised file date/time (last modification) of 2nd file by `wmic`:
for /F "skip=1" %%K in ('wmic DataFile where "Name='!FILE2!'" get LastModified') do for /F %%K in ("%%K") do set "DATE2=%%K"
rem // Compare file dates/times (last mod.) of both files and return differing ones:
if !DATE1! leq !DATE2! echo "!FILE1:\\=\!" is older than "!FILE2:\\=\!"
if !DATE1! gtr !DATE2! echo "!FILE1:\\=\!" is newer than "!FILE2:\\=\!"
if !DATE1! equ !DATE2! echo "!FILE1:\\=\!" and "!FILE2:\\=\!" are of same age
endlocal
)
rem // Clean up temporary files:
del "%_TMP1%" "%_TMP2%"
endlocal
exit /B
The core commands are:
findstr, to return all files that are common in the two given directories;
wmic, to retrieve the last modification dates/times of the files in a standardised manner (formatted like YYYYMMDDhhmmss.uuuuuu+ZZZ), which can be easily compared as strings;
A PowerShell solution using LastWriteTime to compare.
$Dir1 = 'X:\Directory1\'
$Dir2 = 'X:\Directory2\'
ForEach ($File1 in (Get-ChildItem -Path $Dir1 -File |
Where-Object Name -match "^(INV|SLS)")){
$File2 = Join-Path $Dir2 $File1.Name
If (Test-Path $File2 -NewerThan $File1.LastWriteTime){
"File {0} is newer {1} in {2}" -f $File1.Name,(Get-Item $File2).LastWriteTime,$Dir2
} else {
"File {0} is newer {1} in {2}" -f $File1.Name,$File1.LastWriteTime,$Dir1
}
}
Sample output (with my locale datetime format yyyy-MM-dd HH:mm:ss):
File INVF021 is newer 2019-03-19 00:00:00 in X:\Directory2\
File INVF032 is newer 2019-03-19 00:00:00 in X:\Directory2\
File SLS0234 is newer 2019-03-19 00:00:00 in X:\Directory2\
File SLS3211 is newer 2019-03-19 00:00:00 in X:\Directory2\

Counting files and directories at each directory level from a batch file

I have successfully used a batch file which counts the total number of files and directories in a root directory.
Directory structure:
Here is the current script: (gets the number of files and folders returning subfolders up to the nth child).
#echo off
set "drive=D:\Download\app"
for /d %%r in ("%drive%\*") do (
echo Path: %%~fr
for /F "tokens=1,2,3 delims= " %%i in ('dir/a/s %%~fr ^| find /i "bytes"') do if "%%j"=="File(s)" (
set numfiles=%%i
)ELSE (
for /f %%a in ('dir /b /s /ad %%~fr ^|find /c /v "" ') do set numfolders=%%a)
echo Files: %numfiles%
echo Folds: %numfolders%
)
First the program outputs the total number of files and total number of folders in the root directory and then it goes to first subfolder and outputs the same for it's whole tree, then it moves to the next folder at that level etc.
EDIT
I have done the part where it go to 1 level of subfolders and get the total number of files and folders but I want it up to N number of subfolders which mean it should output total number for each and every folder in root directory.
Here is the extended code.
#echo off
setLocal EnableDelayedExpansion
set "drive=C:\Users\%USERNAME%\Downloads\Sandukchi"
set numfiles=
set numfolders=
set count=0;
for /d %%r in ("%drive%\*") do (
echo %%r
SET /A count=count + 1
for /d %%a in ("%%r\*") do set modifiedDate=%%~ta
for /F "tokens=1,2,3 delims= " %%i in ('dir/a/s "%%r\*" ^| find /i "File(s)"') do set fileSizeBytes=%%k
for %%* in ("%%r") do set folderName=%%~nx*
for /F "tokens=1,2,3 delims= " %%i in ('dir/a/s "%%r\*" ^| find /i "bytes"') do if "%%j"=="File(s)" (
set numfiles=%%i
)ELSE (
for /f %%a in ('dir /b /s /ad "%%r\*" ^|find /c /v "" ') do set numfolders=%%a)
echo Last Modified Date: !modifiedDate!
echo Folder Size: !fileSizeBytes! KB
echo Total Number of Files: !numfiles!
echo Total Number of Folders: !numfolders!
(
echo !count! %%r !folderName! !modifiedDate! Total Size !fileSizeBytes!KB Total Files !numfiles! Total Folder !numfolders!
echo.
)>>output.txt
)
#ECHO Off
SETLOCAL
SET "sourcedir=."
SET "tempfile=%temp%\##__##.txt"
SET "dirname="
(
FOR /f "tokens=1,2,*delims= " %%w IN (
'dir /s "%sourcedir%\*" '
) DO (
IF "%%w"=="Directory" (
SET "dirname=%sp256%%%y"&SET /a fcnt=0&SET /a dcnt=-2
) ELSE (
FOR /f "delims= " %%p IN ("%%y") DO (
IF "%%p"=="<DIR>" SET /a dcnt+=1
)
)
IF "%%x"=="File(s)" CALL ECHO %%dirname%%*%%w*%%dcnt%%
)
)>"%tempfile%"
FOR /f "tokens=1,2,*delims=*" %%a IN ('sort "%tempfile%"') DO ECHO directory %%a&ECHO files %%b&echo subdirs %%c
GOTO :EOF
You would need to change the setting of sourcedir to suit your circumstances.
Generates a tempfile. No attempt made to delete the tempfile as it may contain useful data.
Run a standard dir/s command, and pick out the lines which start directory (which indicates a new directoryname) and those where the third space-delimited token is <DIR> for counting subdirectories. When the lines with second token File(s) appears, output the name, filecount and directorycount to a tempfile.
Sort the tempfile and report.
Note: %%y contains the third token onwards from each line. This is retokenised, selecting the first token only (default) to %%p isolating the third token of the original line.
The tempfile is produced using * as a separator since * is not a valid filename character.
dcnt is set to -2 to start the count-of-directories because both . and .. are reported as directorynames in a dir /s.
Give a try for this code :
#echo off
Setlocal EnableDelayedExpansion
#For /D %%D in (*) DO (
Set "Folder=%%~D"
PUSHD "!Folder!"
FOR /F %%H in ('dir /a-d /b 2^>NUL^|find /C /V "" ') DO ( Set "numFiles=%%H" )
FOR /F %%I in ('dir /ad /b 2^>NUL^|find /C /V "" ') DO ( Set "numSubFolders=%%I" )
POPD
echo The Folder "!Folder!" has !numSubFolders! SubFolders and !numFiles! Files
)
)
pause & exit

Resources