Comparing the contents of two recently created folders using Windows Batch file - batch-file

I'm writing a batch file to compare the contents of two folders on a network drive. A new folder is generated every night by a macro and I want to see what changed between today and yesterday. For example, if yesterday's folder is called "B" and today's folder is "A" and their structure looks like:
- Home
- A
- file1.txt
- file2.txt
- file4.txt
- B
- file1.txt
- file2.txt
- file3.txt
I would want to see something like
A: file4.txt added
A: file3.txt removed
But the format of the output doesn't really matter at the end of the day. I just need to see a comparison of the folder's contents.
What I have so far
Using my limited batch knowledge, I've smashed together this currently non-working solution:
#ECHO OFF
setlocal EnableDelayedExpansion
pushd "\\domain\path\to\Home"
set "j=0"
set "count=2"
:: get the names of the two most recently added folders
FOR /f "delims=" %%i IN ('dir /AD-H /B /O-D') DO (
set /A j=j+1
if !j! equ 1 (
:: send contents of newest folder to file
dir !i! /B > newest_folder.txt
)
if !j! equ 2 (
:: send contents of second-newest folder to file
dir !i! /B > older_folder.txt
)
if !j! geq !count! (
:: break after two folders
goto :end
)
)
:end
fc newest_folder.txt oldest_folder.txt
PAUSE
I saw a similar solution here:
(for %%i in ("folder2\*") do if exist "folder1\%%~nxi" echo(%%~i)>file.csv
But it wouldn't work in my case because the folder names change every day.
Any help would be appreciated!

#ECHO OFF
setlocal EnableDelayedExpansion
set "later="
set "earlier="
pushd "\\domain\path\to\Home"
:: get the names of the two most recently added folders
FOR /f "delims=" %%i IN ('dir /AD-H /B /O-D') DO (
if defined later set "earlier=%%i"&goto report
set "later=%%i"
)
:report
if defined earlier (
dir /b "%earlier%">older.txt
dir /b "%later%">newer.txt
fc older.txt newer.txt
) else echo too few directories found
pause
popd
would be my approach, setting later and earlier to the names of the first two directories found by the dir command.
---- afterthought
if the fc command is replaced by
echo files added
findstr /x /v /g:"older.txt" "newer.txt"
echo files deleted
findstr /x /v /g:"newer.txt" "older.txt"
then your report should be more easily interpreted.
findstr options /x does an exact match, /v reports NON-matches and /g:filename specifies a file containing strings against which to match.

Related

CMD: "echo." new line character doesn't work (merge .txt files with line break)

I have a bunch of .txt files:
1.txt containing string "1"
2.txt containing string "2"
3.txt containing string "3"
I need to combine them to get "result.txt" containing this:
1
2
3
I used echo. as a new line character in a batch:
for /r %%i in (*.txt) do (
type %%i>> result.txt
echo.>> result.txt
)
But in "result.txt" I'm getting this:
1
2.
3.
So, echo. actually works perfectly well for the first (1.txt) file, but it also puts a . before new lines for the rest of the files.
Can someone please fix batch code for me?
P.S.: the problem occurred because .txt files were located in different subfolders - that's why I used for /r initially (it doesn't always work, see details below!).
Thanks #Mofi, see his comments below the starting post!
This is a solution for а Folder on (any) user's Desktop:
for /f "delims=" %%i in ('dir "%UserProfile%\Desktop\Folder\*.txt" /a-d /b /on /s 2^>nul') do (
echo %%~fi>> result.txt
type "%%i">> result.txt
echo(>> result.txt
)
echo %%~fi>> result.txt adds full path to any *.txt file found in
that folder (and subfolders);
type "%%i">> result.txt merges found *.txt files
contents;
echo(>> result.txt is the new line character requested!
This code tested on NTFS and is promised to work on FAT\FAT32 etc. - unlike for /R (see #Mofi comments below the starting post!).
P.S.: you can launch this batch code from any location; also, use echo {anytext}>> result.txt after echo(>> result.txt to easily navigate between breaks in "result.txt" :)
P.P.S.: also, if you want to get rid of blank line in the EOF "result.txt" - use this:
set /a counter1=0
set /a counter2=0
::cycle 1
for /f "delims=" %%i in ('dir "%UserProfile%\Desktop\Folder\*.txt" /a-d /b /on /s 2^>nul') do (
set /a counter1+=1
)
::cycle 2
setlocal enableextensions enabledelayedexpansion
for /f "delims=" %%i in ('dir "%UserProfile%\Desktop\Folder\*.txt" /a-d /b /on /s 2^>nul') do (
echo %%~fi>> result.txt
type "%%i">> result.txt
set /a counter2+=1
if not !counter2!==%counter1% (echo(>> result.txt)
)
endlocal
Inspired by: https://stackoverflow.com/a/7522822/6859021
both cycles has the same for /f conditions;
::cycle 1 counts number of files that are about to undergo the procedure;
::cycle 2 is identical to original solution, except for "Inspired
by" details;
::* lines are comments: they don't affect code
execution;
if not !counter2!==%counter1% (echo(>> result.txt) merely puts echo( for all lines, except for the last one! :D
Notice however that counters will work properly only if "result.txt" will NOT be located inside Folder of interest, or it's subfolders.
First: I could not reproduce the problem where additional dots are added to the file.
However I found a different issue, that might have to do something with it. When you are iterating over all text-files, you get the result.txt at one point as well. (And other files you might add or change during runtime.)
So I created this basic script, which does exactly what you described as your desired behavior:
#echo off
:: clear
rd testdir /s /q 2>nul
:: create files
md testdir
cd testdir
for /l %%i in (1 1 3) do (
<nul set /p "=%%i">"%%i.txt"
)
:: combine the files
for /r %%f in (*.txt) do (
if not "%%~nf"=="result" (
type "%%f">>result.txt
echo.>>result.txt
)
)
:: show result
echo result.txt
type result.txt
pause

Batch file to create folder and move file in the folder

I am in the middle of batch extracting screenshots for contents we are planning to use on a tube site I am working on.
The jpeg files per content is labled as followed:
6c82c0239f6eb839-1
6c82c0239f6eb839-2
all the way to 120
The file name is different per content
a82384e2c46ba4af-1
a82384e2c46ba4af-2
etc.
They will all be extracted to a singe folder.
So I basically need a batch file that will create folders based on the content name without the dash and number and move all 120 jpegs in the folder with the content name.
For example:
Create folder named 6c82c0239f6eb839 and
move 6c82c0239f6eb839-1 to 6c82c0239f6eb839-120 in to the created folder.
I saw another thread with the following batch file. its pretty much what I want but the folder name is only 3 characters long and the files are copied to the newly created folders instead of moving them.
#echo off
SetLocal EnableDelayedExpansion
for /F "delims=" %%a in ('dir /b *.jpeg') do (
set Name=%%a
set Folder=!Name:~0,3!
xcopy /y "%%a" !Folder!\
)
Could someone change this so that it will display full file name without the dash and number for the folders and move files in its respective folders instead of copy?
Thank you
#echo off
setlocal
#rem Get each jpeg file.
for /F "delims=" %%A in ('2^>nul dir /b *.jpeg') do (
rem Get filename as token before the dash.
for /f "delims=-" %%B in ("%%~A") do (
rem Make dir if needed.
if not exist "%%~B" md "%%~B"
rem Check if isdir.
2>nul pushd "%%~B" && popd
if errorlevel 1 (
>&2 echo Failed isdir "%%~B".
) else (
rem Do the move operation.
>nul move /y "%%~A" "%%~B"
if errorlevel 1 (
>&2 echo Failed move "%%~A" to "%%~B"
)
)
)
)
exit /b %errorlevel%
The code is well remarked so if you want to understand
the evaluated code by changing #echo off to #echo on.
The use of %errorlevel% after the exit /b is not
required though will let you know what the errorlevel is
when #echo on is used.
The pushd tests for a directory
(even if it is a symlink).
errorlevel is checked to decide if to echo a
error message or do the move.
As the for loop variables are used direct, use of
enabledelayedexpansion is not needed.
Many commands support the argument of /? to get help
about the command. i.e. move /?.
If you only try to copy the correct jpeg to the correct folder, you can do this:
#echo off
SetLocal EnableDelayedExpansion
CD <CORRECT ROOT PATH>
for /F "delims=" %%a in ('dir /b *.jpeg') do (
set Name=%%a
REM I presume all the files have 16 characters before the dash
set Folder=!Name:~0,16!
IF NOT EXIST !Folder! MKDIR !FOLDER!
xcopy /y "%%a" !Folder!\
)
I was not able to test.
First of all, I would like to apologize for my manners regarding my initial post.
The answer by micheal_heath has resolved my issue.
Furthermore, I happened to find this post by user Salmon Trout from a different site which also worked.
Batch file to make folders with part of file name and then copy files
#echo off
setlocal enabledelayedexpansion
for %%A in (*.psd *.jpg) 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=1* delims=_" %%D in ("!fname!") do set folname=%%D
echo folder name !folname!
if not exist "!folname!" (
echo Folder !folname! does not exist, creating
md "!folname!"
) else (
echo Folder !folname! exists
)
echo Moving file %%A to folder !folname!
move "%%A" "!folname!"
)
echo Finished
pause
I just changed the the following line remove the hypen and numbers to create folders for the file name properly.
for /f "tokens=1* delims=-***" %%D in ("!fname!") do set folname=%%D
I still lack the knowledge on why and how both methods work, but this has been an interesting start for me. I hope other beginners trying to solve a similar issue can find something useful from this post.

Count a number of pdf files in every subfolder separately in batch

Under the path U:\test\0014* I have 99 folders and each of them has respectively 2 subfolders MASTER and DERIVATIVE_COPY. With a following script I try to count the number of PDFs in MASTER folder. If there are only one .pdf file there I want to copy it to DERIVATIVE_COPY folder. IF there are 0 or >1 .pdf in MASTER I want only to show the number of them. This operation should be done for each of the 99 folders.
#echo off
setlocal enabledelayedexpansion
for /R U:\test\0014\*\MASTER %%i in (*.pdf) do (
set /a anzahl+=1
)
if !anzahl! EQU 1 ( echo !anzahl! )
if NOT !anzahl! EQU 1 ( echo !anzahl! )
pause
#ECHO OFF
SETLOCAL enabledelayedexpansion
SET "sourcedir=U:\sourcedir"
for /f %%i in ('dir /ad /s/b "%sourcedir%\master*"') do IF /i "%%~nxi"=="MASTER" (
SET /a found=0
FOR %%x IN ("%%i\*.pdf") DO SET /a found+=1
IF !found!==1 (
XCOPY /y "%%i\*.pdf" "%%i\..\derivative_copy\" >nul
) ELSE (
ECHO !found! .pdf files found IN "%%i"
)
)
GOTO :EOF
You would need to change the setting of sourcedir to suit your circumstances.
First, execute a dir to get a directory-list of the "files" names that start master in the specified tree. Use /ad to select only the directory-names. Accept only those names where the "name+extension" is master (disregarding case)
For each directory-name found, set found to 0 then increment found for each .pdf file found in the directory %%i.
If the resultant count in found is 1, xcopy the file found to the destination subdirectory (which conveniently creates the subdirectory if it doesn't already exist), using /y to overwrite any existing file of the same name and >nul to make the process silent.
Otherwise, report the directoryname and count of files.

Windows Batch - get folder name inside a directory [duplicate]

How can I iterate over each file in a directory using a for loop?
And how could I tell if a certain entry is a directory or if it's just a file?
This lists all the files (and only the files) in the current directory and its subdirectories recursively:
for /r %i in (*) do echo %i
Also if you run that command in a batch file you need to double the % signs.
for /r %%i in (*) do echo %%i
(thanks #agnul)
Iterate through...
...files in current dir: for %f in (.\*) do #echo %f
...subdirs in current dir: for /D %s in (.\*) do #echo %s
...files in current and all subdirs: for /R %f in (.\*) do #echo %f
...subdirs in current and all subdirs: for /R /D %s in (.\*) do #echo %s
Unfortunately I did not find any way to iterate over files and subdirs at the same time.
Just use cygwin with its bash for much more functionality.
Apart from this: Did you notice, that the buildin help of MS Windows is a great resource for descriptions of cmd's command line syntax?
Also have a look here: http://technet.microsoft.com/en-us/library/bb490890.aspx
To iterate over each file a for loop will work:
for %%f in (directory\path\*) do ( something_here )
In my case I also wanted the file content, name, etc.
This lead to a few issues and I thought my use case might help. Here is a loop that reads info from each '.txt' file in a directory and allows you do do something with it (setx for instance).
#ECHO OFF
setlocal enabledelayedexpansion
for %%f in (directory\path\*.txt) do (
set /p val=<%%f
echo "fullname: %%f"
echo "name: %%~nf"
echo "contents: !val!"
)
*Limitation: val<=%%f will only get the first line of the file.
There is a subtle difference between running FOR from the command line and from a batch file. In a batch file, you need to put two % characters in front of each variable reference.
From a command line:
FOR %i IN (*) DO ECHO %i
From a batch file:
FOR %%i IN (*) DO ECHO %%i
This for-loop will list all files in a directory.
pushd somedir
for /f "delims=" %%f in ('dir /b /a-d-h-s') do echo %%f
popd
"delims=" is useful to show long filenames with spaces in it....
'/b" show only names, not size dates etc..
Some things to know about dir's /a argument.
Any use of "/a" would list everything, including hidden and system attributes.
"/ad" would only show subdirectories, including hidden and system ones.
"/a-d" argument eliminates content with 'D'irectory attribute.
"/a-d-h-s" will show everything, but entries with 'D'irectory, 'H'idden 'S'ystem attribute.
If you use this on the commandline, remove a "%".
Hope this helps.
%1 refers to the first argument passed in and can't be used in an iterator.
Try this:
#echo off
for %%i in (*.*) do echo %%i
I had trouble getting jop's answer to work with an absolute path until I found this reference: https://ss64.com/nt/for_r.html
The following example loops through all files in a directory given by the absolute path.
For /R C:\absoulte\path\ %%G IN (*.*) do (
Echo %%G
)
Here's my go with comments in the code.
I'm just brushing up by biatch skills so forgive any blatant errors.
I tried to write an all in one solution as best I can with a little modification where the user requires it.
Some important notes: Just change the variable recursive to FALSE if you only want the root directories files and folders processed. Otherwise, it goes through all folders and files.
C&C most welcome...
#echo off
title %~nx0
chcp 65001 >NUL
set "dir=c:\users\%username%\desktop"
::
:: Recursive Loop routine - First Written by Ste on - 2020.01.24 - Rev 1
::
setlocal EnableDelayedExpansion
rem THIS IS A RECURSIVE SOLUTION [ALBEIT IF YOU CHANGE THE RECURSIVE TO FALSE, NO]
rem By removing the /s switch from the first loop if you want to loop through
rem the base folder only.
set recursive=TRUE
if %recursive% equ TRUE ( set recursive=/s ) else ( set recursive= )
endlocal & set recursive=%recursive%
cd /d %dir%
echo Directory %cd%
for %%F in ("*") do (echo → %%F) %= Loop through the current directory. =%
for /f "delims==" %%D in ('dir "%dir%" /ad /b %recursive%') do ( %= Loop through the sub-directories only if the recursive variable is TRUE. =%
echo Directory %%D
echo %recursive% | find "/s" >NUL 2>NUL && (
pushd %%D
cd /d %%D
for /f "delims==" %%F in ('dir "*" /b') do ( %= Then loop through each pushd' folder and work on the files and folders =%
echo %%~aF | find /v "d" >NUL 2>NUL && ( %= This will weed out the directories by checking their attributes for the lack of 'd' with the /v switch therefore you can now work on the files only. =%
rem You can do stuff to your files here.
rem Below are some examples of the info you can get by expanding the %%F variable.
rem Uncomment one at a time to see the results.
echo → %%~F &rem expands %%F removing any surrounding quotes (")
rem echo → %%~dF &rem expands %%F to a drive letter only
rem echo → %%~fF &rem expands %%F to a fully qualified path name
rem echo → %%~pF &rem expands %%A to a path only
rem echo → %%~nF &rem expands %%F to a file name only
rem echo → %%~xF &rem expands %%F to a file extension only
rem echo → %%~sF &rem expanded path contains short names only
rem echo → %%~aF &rem expands %%F to file attributes of file
rem echo → %%~tF &rem expands %%F to date/time of file
rem echo → %%~zF &rem expands %%F to size of file
rem echo → %%~dpF &rem expands %%F to a drive letter and path only
rem echo → %%~nxF &rem expands %%F to a file name and extension only
rem echo → %%~fsF &rem expands %%F to a full path name with short names only
rem echo → %%~dp$dir:F &rem searches the directories listed in the 'dir' environment variable and expands %%F to the fully qualified name of the first one found. If the environment variable name is not defined or the file is not found by the search, then this modifier expands to the empty string
rem echo → %%~ftzaF &rem expands %%F to a DIR like output line
)
)
popd
)
)
echo/ & pause & cls
To iterate through all files and folders you can use
for /F "delims=" %%a in ('dir /b /s') do echo %%a
To iterate through all folders only not with files, then you can use
for /F "delims=" %%a in ('dir /a:d /b /s') do echo %%a
Where /s will give all results throughout the directory tree in unlimited depth. You can skip /s if you want to iterate through the content of that folder not their sub folder
Implementing search in iteration
To iterate through a particular named files and folders you can search for the name and iterate using for loop
for /F "delims=" %%a in ('dir "file or folder name" /b /s') do echo %%a
To iterate through a particular named folders/directories and not files, then use /AD in the same command
for /F "delims=" %%a in ('dir "folder name" /b /AD /s') do echo %%a
for %1 in (*.*) do echo %1
Try "HELP FOR" in cmd for a full guide
This is the guide for XP commands. http://www.ss64.com/nt/
The following code creates a file Named "AllFilesInCurrentDirectorylist.txt" in the current Directory, which contains the list of all files (Only Files) in the current Directory. Check it out
dir /b /a-d > AllFilesInCurrentDirectorylist.txt
It could also use the forfiles command:
forfiles /s
and also check if it is a directory
forfiles /p c:\ /s /m *.* /c "cmd /c if #isdir==true echo #file is a directory"
I would use vbscript (Windows Scripting Host), because in batch I'm sure you cannot tell that a name is a file or a directory.
In vbs, it can be something like this:
Dim fileSystemObject
Set fileSystemObject = CreateObject("Scripting.FileSystemObject")
Dim mainFolder
Set mainFolder = fileSystemObject.GetFolder(myFolder)
Dim files
Set files = mainFolder.Files
For Each file in files
...
Next
Dim subFolders
Set subFolders = mainFolder.SubFolders
For Each folder in subFolders
...
Next
Check FileSystemObject on MSDN.
I use the xcopy command with the /L option to get the file names. So if you want to get either a directory or all the files in the subdirectory you could do something like this:
for /f "delims=" %%a IN ('xcopy "D:\*.pdf" c:\ /l') do echo %%a
I just use the c:\ as the destination because it always exists on windows systems and it is not copying so it does not matter. if you want the subdirectories too just use /s option on the end. You can also use the other switches of xcopy if you need them for other reasons.
Try this to test if a file is a directory:
FOR /F "delims=" %I IN ('DIR /B /AD "filename" 2^>^&1 ^>NUL') DO IF "%I" == "File Not Found" ECHO Not a directory
This only will tell you whether a file is NOT a directory, which will also be true if the file doesn't exist, so be sure to check for that first if you need to. The carets (^) are used to escape the redirect symbols and the file listing output is redirected to NUL to prevent it from being displayed, while the DIR listing's error output is redirected to the output so you can test against DIR's message "File Not Found".
try this:
::Example directory
set SetupDir=C:\Users
::Loop in the folder with "/r" to search in recursive folders, %%f being a loop ::variable
for /r "%SetupDir%" %%f in (*.msi *.exe) do set /a counter+=1
echo there are %counter% files in your folder
it counts .msi and .exe files in your directory (and in the sub directory). So it also makes the difference between folders and files as executables.
Just add an extension (.pptx .docx ..) if you need to filter other files in the loop
In my case I had to delete all the files and folders underneath a temp folder. So this is how I ended up doing it. I had to run two loops one for file and one for folders. If files or folders have spaces in their names then you have to use " "
cd %USERPROFILE%\AppData\Local\Temp\
rem files only
for /r %%a in (*) do (
echo deleting file "%%a" ...
if exist "%%a" del /s /q "%%a"
)
rem folders only
for /D %%a in (*) do (
echo deleting folder "%%a" ...
if exist "%%a" rmdir /s /q "%%a"
)

Rename A Sub-Directory the parent folder name if NOT called "info"

Is it possible to make a batch file that will rename a folder if does not have a specific name?
EG:
Parent Directory
- - - > info
- - - > randomfoldername
I have many folders that follow the above pattern. What I would like to do is make a batch file that will rename "randomfoldername" in this structure. There is always two folders in the Parent Directory, one is always "info" and the other changes for each case. Is there a method within a batch file that I could use to always rename the "randomfoldername" directory? I was thinking something along the lines of,
IF NOT == "info" THEN ren... ect.
Is this possible?
you can first check if folder exist then rename folder
if not exist c:\info ( call random.bat )
random.bat:
dir /A:D /b | findstr.exe /n /R "." > s:\sites\file-count.txt
FOR /F "tokens=1-10 delims=:" %%A IN ('type "s:\sites\file-count.txt"') do
set NUMBER-OF-FILES=%%A
FOR /L %%A IN (1,1,%number-of-files%) DO CALL RENAME.bat %%A
rename.bat
:rename
FOR /F "tokens=1-10 delims=:" %%A IN ('type "s:\sites\file-count.txt" ^|
findstr "%1:"') do ren %%B %RANDOM%%%B
also u can use free tool like Bulk Rename Utility
that app has cli for use in bat file here
also You can use powershell script like
$a = test-path c:\info
if ( $a -eq "True" ) { Write-Host nothing to do } Else { gic -directory path | %{$n="$pwd\$((get-random).tostring())$($_.name)";$_.moveto($N)} }
Next script starts with basic checks on command line parameter passed into.
FOR /F loop against the results of another command (dir) used. Next explanation stolen (see dbenham's original) :
FOR /R (as well as the simple FOR) begin iterating immediately, before
it has finished scanning the disk drive. It is possible for the loop
to reiterate the already named file! This would cause it to be renamed
twice, giving the wrong result. The solution is to use FOR /F with
command 'DIR /B', because FOR /F always processes the command to
completion before iterating.
#ECHO OFF >NUL
SETLOCAL enableextensions disabledelayedexpansion
set "ParentDirectory=%*"
if "%ParentDirectory%"=="" goto :usage1
if not exist "%ParentDirectory%\" goto :usage2
if not exist "%ParentDirectory%\info\" goto :usage3
set "SpecificName=Desired Name"
set /A "count=0"
for /F "delims=" %%G in ('dir /B /A:D "%ParentDirectory%\"') do set /A "count+=1"
if %count% neq 2 goto :usage4
for /F "delims=" %%G in ('dir /B /A:D "%ParentDirectory%\"') do (
if /I not "%%~G"=="info" (
if /I not "%%~G"=="%SpecificName%" (
echo renaming "%ParentDirectory%\%%~G" to "%SpecificName%"
rename "%ParentDirectory%\%%~G" "%SpecificName%"
) else (
echo renamed already: "%SpecificName%"
)
)
)
:endlocal
ENDLOCAL
goto :eof
:usage1
echo no paramater
goto :endlocal
:usage2
echo "%ParentDirectory%" folder does not exist
goto :endlocal
:usage3
echo "%ParentDirectory%\info" folder does not exist
goto :endlocal
:usage4
echo "%ParentDirectory%" folder contains %count% subfolders
goto :endlocal
Output:
==>29376745.bat
no paramater
==>29376745.bat x y
"x y" folder does not exist
==>29376745.bat d:\test
"d:\test\info" folder does not exist
==>md "d:\test\info"
==>29376745.bat d:\test
"d:\test" folder contains 6 subfolders
==>29376745.bat d:\test\29376745
renaming "d:\test\29376745\currentName" to "Desired Name"
==>29376745.bat d:\test\29376745
renamed already: "Desired Name"
==>
Required reading (indexes only):
An A-Z Index of the Windows CMD command line
Windows CMD Shell Command Line Syntax

Resources