I have a folder named G:\Project\Projects
This folder has some(about 20) unknown subfolders. Some of the subfolders have a folder named SSS and a file called deleteme.doc
I want to delete all these SSS folders(with contents) and deleteme.doc files using a batch file or powershell file. I do not want to delete these files or folder deeply nested into the subfolders, just those who are directly under subfolder.
For example I want to delete this folder
G:\Project\Projects\proj 1\sss
but not these 3-
G:\Project\Projects\proj 1\other\sss
G:\Project\Projects\sss
G:\Project\Projects\proj 1\sss (it is a file)
The thing is I want to simply double click-run the batch/powershell file from anywhere, possibly from another partition or drive (or even network computer).
I tried RMDIR G:\Project\Projects\*\SSS it didn't seem to be working.
Please explain your answer if possible, also show me commands for both recycle bin delete and parma delete. Though I think sending to recycle bin is not possible without powershell.
Administrator privilege is not necessary to delete folders in non-system partition right?
Windows 10. powershell version 5
#ECHO Off
SETLOCAL
:: remove variables starting $
FOR /F "delims==" %%a In ('set $ 2^>Nul') DO SET "%%a="
:: set defaults
SET "$dirname=sss"
SET "$filename=deleteme.doc"
SET "$from=c:\wherever\you\like"
:: analyse command line
:: syntax file="filename" " dir=dirname" from="dirname" (/switches)
:: each is optional. default is as established above
:: /d - delete directory only if it is completely empty
:: /dn - delete directory only if it is not completely empty
:: /ds - delete directory only if it is completely empty or if no files in its subdirectory tree
:: /fe - delete file if empty
:: /fn - delete file if not empty
:: /b - both file and directory must exist to make deletions
::
FOR %%a IN (%*) DO IF DEFINED $twopart (CALL :parttwo %%a) ELSE (
SET "$processed="
IF /i "%%a"=="dir" SET "$processed=Y"&SET "$twopart=$dirname"
IF /i "%%a"=="file" SET "$processed=Y"&SET "$twopart=$filename"
IF /i "%%a"=="from" SET "$processed=Y"&SET "$twopart=$from"
IF /i "%%a"=="/b" CALL :setswitch b
IF /i "%%a"=="/d" CALL :setswitch d
IF /i "%%a"=="/dn" CALL :setswitch dn
IF /i "%%a"=="/ds" CALL :setswitch ds
IF /i "%%a"=="/fe" CALL :setswitch fe
IF /i "%%a"=="/fn" CALL :setswitch fn
IF NOT DEFINED $processed CALL :extras %%a
)
IF DEFINED $extras ECHO(%$extras% NOT understood)&pause&GOTO :EOF
:: resolve switch compatibility
IF DEFINED $d IF DEFINED $dn SET "$incompatible=/d /dn"
IF DEFINED $fe IF DEFINED $fn SET "$incompatible=%$incompatible% /f /fn"
IF DEFINED $incompatible ECHO(%$incompatible% incompatible)&pause&GOTO :EOF
:: if $from is set, make it quoted.
IF DEFINED $from SET "$from="%$from:"=%""
:: Now search for the directory
FOR /d /r %$from% %%a IN ("%$dirname%") DO IF EXIST "%%a\." (
SET "$victim=%%a"
CALL :deldir
)
IF NOT DEFINED $b FOR /r %$from% %%a IN ("%$filename%") DO IF EXIST "%%a" (
SET "$victim=%%~dpa%$filename%"
CALL :delfile
)
GOTO :eof
:: delete the victim directory if all other conditions are met
:: take care of file as well if required.
:deldir
SET "$victim=%$victim:"=%"
IF DEFINED $b IF NOT EXIST "%$victim%\..\%$filename%" GOTO :eof
:: if the directory has any contents and "/d" was specified, skip deletion
IF DEFINED $d (
FOR /f %%z IN ('dir /b "%$victim%" 2^>nul') DO GOTO :eof
)
:: if the directory has files in its subtree and "/ds" was specified, skip deletion
IF DEFINED $ds (
FOR /f %%z IN ('dir /s /b /a-d "%$victim%" 2^>nul') DO GOTO :eof
)
:: if the directory has no files and none in its subtree and "/dn" was specified, don't skip deletion
IF DEFINED $dn (
FOR /f %%z IN ('dir /b "%$victim%" 2^>nul') DO GOTO deldir1
GOTO :eof
)
:: delete the directory
:deldir1
ECHO(DEL /s /f /q "%$victim%"
SET "$victim=%$victim%\..\%$filename%"
IF EXIST "%$victim%" GOTO delfile
GOTO :eof
:delfile
FOR %%z IN ("%$victim%") DO (
IF DEFINED $fn IF "%%~zz"=="0" GOTO :EOF
IF DEFINED $fe IF "%%~zz" neq "0" GOTO :EOF
)
ECHO(DEL /f /q "%$victim%"
GOTO :eof
:parttwo
SET "%$twopart%=%~1"
SET "$twopart="
GOTO :eof
:extras
SET "$extras=%$extras% %~1"
GOTO :eof
:setswitch
SET "$processed=Y"
SET "$%1=Y"
SHIFT
IF "%1" neq "" GOTO setswitch
GOTO :EOF
Here's a general solution which you'd need to test before unleashing.
The required RD commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(RD to RD to actually delete the directories.
The required DEL commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(DEL to DEL to actually delete the files.
The first section analyses the parameters ans switches supplied to the program.
The defaults are shown and need to be adjusted to suit your circumstances.
The command may be run as
*thisname* from=c:\new\location dir=xxx file=hello.txt /d
where from specifies where the tree-walk begins, fie the filename to be located and dir the directoryname to delete.
The switches are also listed.
The program simply examines the command line for elements of interest and sets values as required.
We then traverse the tree, looking for the directoryname and then make decisions depending on the switch-settings.
Then traverse the tree again, looking for the filename.
Something like
for /f %A in ('dir "c:\somewhere\deleteme.doc"') Do echo rd "%~dpA"
Administrator privilege is not necessary to delete folders in non-system partition right?
How would we know what permissions you have. Type icacls c:\somewhere to see.
You need to know your problem.
I created a short powershell solution using this-
$targetName='ss s';
foreach ($file in Get-Childitem "G:\Project\Projects\" )
{
$fname=$file.name;
if (Test-Path "G:\Project\Projects\$fname\$targetName\")
{
$shell = new-object -comobject "Shell.Application"
$item = $shell.Namespace(0).ParseName("G:\Project\Projects\$fname\$targetName")
$item.InvokeVerb("delete")
}
}
This powershell script sends that folder to recycle bin after confirmation popup. (this won't send any file named 'ss s')
this seems to be working for batch file script-
set "b=ss s"
for /d %%a in (*) do IF EXIST "%%a\%b%\*" (rmdir /s "%%a\%b%")
if the targetfolder was named "$ss s" then you have to set variables as "b=/$ss s"
Related
I have a directory with files and a ControlFile.txt that contains the a list of SHA256 sums for various files. I'm trying to come up with a Batch process to loop through the files in the directory, calculate the SHA256 value of each file, and then compare whether or not the calculated SHA256 exists or not in the ControlFile.txt and branch accordingly.
I've attempted to produce a working script with the following but I believe I'm missing some key elements:
for /R . %%f in (*.*) do (
find /c "(certutil -hashfile "%%f" SHA256 | findstr /V "hash")" ControlFile.txt > NUL
if %errorlevel% equ 1 goto notfound
echo "%%f" found
goto done
:notfound
echo "%%f" notfound
goto done
:done)
I believe I may need to set a variable for the given SHA256 value and use that within the loop to produce the comparative function I'm trying to achieve, but my knowledge with batch files and cmd is limited. Any code advice would be greatly appreciated.
#ECHO OFF
SETLOCAL
rem The following settings for the source directory and filename are names
rem that I use for testing and deliberately include names which include spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files"
SET "filename1=%sourcedir%\q74148620.txt"
FOR /f "delims=" %%b IN ('dir /s /b /a-d "u:\j*" ') DO (
FOR /f %%y IN ('certutil -hashfile "%%b" SHA256 ^| find /V ":"') do (
findstr /x "%%y" "%filename1%" > NUL
IF ERRORLEVEL 1 (
ECHO "%%b" NOT found
) ELSE (
ECHO "%%b" found
)
)
)
GOTO :EOF
I used a filemask of j* for testing - change to suit.
Simply run the certutil routine on each file in turn, filter out any lines that contain :, leaving the SHA256 data. Locate that value as /x an exact mach to a line in the SHA256 values file. If a match is found, errorlevel is set to 0, non-0 otherwise, then switch on errorlevel.
=== minor revision to not include subdirectories ===
#ECHO OFF
SETLOCAL
rem The following settings for the source directory and filename are names
rem that I use for testing and deliberately include names which include spaces to make sure
rem that the process works using such names. These will need to be changed to suit your situation.
SET "sourcedir=u:\your files"
SET "filename1=%sourcedir%\q74148620.txt"
PUSHD "%sourcedir%"
FOR /f "delims=" %%b IN ('dir /b /a-d') DO (
FOR /f %%y IN ('certutil -hashfile "%%b" SHA256 ^| find /V ":"') do (
findstr /x "%%y" "%filename1%" > NUL
IF ERRORLEVEL 1 (
ECHO "%%b" NOT found
) ELSE (
ECHO "%%b" found
)
)
)
POPD
GOTO :EOF
The dir options /b shows the names only (not size, date, etc.), /s would scan subdirectories and also generate the filenames with their full path, and /a-d suppresses directory names. u:\j* is the start location for the listing; drive u:, all files that begin j (that was for testing).
The pushd command makes the nominated directory current, so the revised dir command will scan only that directory for all filenames but no directorynames (since no starting directory and filemask is provided).
The popd command returns to the original directory.
I want to create a .bat script to copy only one random file from each folder (also subfolders, so recursively) whilst also keeping the folder structure. I've tried the following code which comes close to what I want but doesn't copy the folder structure and one file per folder.
#ECHO OFF
SETLOCAL EnableExtensions EnableDelayedExpansion
SET Destination=H:\Temp
SET FileFilter=.ape
SET SubDirectories=/S
SET Source=%~dp1
SET FileList1Name=FileList1.%RANDOM%.txt
SET FileList1="%TEMP%\%FileList1Name%"
SET FileList2="%TEMP%\FileList2.%RANDOM%.txt"
ECHO Source: %Source%
IF /I {%SubDirectories%}=={/S} ECHO + Sub-Directories
IF NOT {"%FileFilter%"}=={""} ECHO File Filter: %FileFilter%
ECHO.
ECHO Destination: %Destination%
ECHO.
ECHO.
ECHO Building file list...
CD /D "%Source%"
DIR %FileFilter% /A:-D-H-S /B %SubDirectories% > %FileList1%
FOR /F "tokens=1,2,3 delims=:" %%A IN ('FIND /C ":" %FileList1%') DO SET TotalFiles=%%C
SET TotalFiles=%TotalFiles:~1%
ECHO The source has %TotalFiles% total files.
ECHO Enter the number of random files to copy to the destination.
SET /P FilesToCopy=
ECHO.
IF /I %TotalFiles% LSS %FilesToCopy% SET %FilesToCopy%=%TotalFiles%
SET Destination="%Destination%"
IF NOT EXIST %Destination% MKDIR %Destination%
SET ProgressTitle=Copying Random Files...
FOR /L %%A IN (1,1,%FilesToCopy%) DO (
TITLE %ProgressTitle% %%A / %FilesToCopy%
REM Pick a random file.
SET /A RandomLine=!RANDOM! %% !TotalFiles!
REM Go to the random file's line.
SET Line=0
FOR /F "usebackq tokens=*" %%F IN (%FileList1%) DO (
IF !Line!==!RandomLine! (
REM Found the line. Copy the file to the destination.
XCOPY /V /Y "%%F" %Destination%
) ELSE (
REM Not the random file, build the new list without this file included.
ECHO %%F>> %FileList2%
)
SET /A Line=!Line! + 1
)
SET /A TotalFiles=!TotalFiles! - 1
REM Update the master file list with the new list without the last file.
DEL /F /Q %FileList1%
RENAME %FileList2% %FileList1Name%
)
IF EXIST %FileList1% DEL /F /Q %FileList1%
IF EXIST %FileList2% DEL /F /Q %FileList2%
ENDLOCAL
The destination should be set in the .bat code like the code above. Can anybody please help me with this? Thanks in advance!
Copying a directory tree structure (folders only) is trivial with XCOPY.
Selecting a random file from a given folder is not too difficult. First you need the count of files, using DIR /B to list them and FIND /C to count them. Then use the modulo operator to select a random number in the range. Finally use DIR /B to list them again, FINDSTR /N to number them, and another FINDSTR to select the Nth file.
Perhaps the trickiest bit is dealing with relative paths. FOR /R can walk a directory tree, but it provides a full absolute path, which is great for the source, but doesn't do any good when trying to specify the destination.
There are a few things you could do. You can get the string length of the root source path, and then use substring operations to derive the relative path. See How do you get the string length in a batch file? for methods to compute string length.
Another option is to use FORFILES to walk the source tree and get relative paths directly, but it is extremely slow.
But perhaps the simplest solution is to map unused drive letters to the root of your source and destination folders. This enables you to use the absolute paths directly (after removing the drive letter). This is the option I chose. The only negative aspect of this solution is you must know two unused drive letters for your system, so the script cannot be simply copied from one system to another. I suppose you could programatically
discover unused drive letters, but I didn't bother.
Note: It is critical that the source tree does not contain the destination
#echo off
setlocal
:: Define source and destination
set "source=c:\mySource"
set "destination=c:\test2\myDestination"
:: Replicate empty directory structure
xcopy /s /t /e /i "%source%" "%destination%"
:: Map unused drive letters to source and destination. Change letters as needed
subst y: "%source%"
subst z: "%destination%"
:: Walk the source tree, calling :processFolder for each directory.
for /r y:\ %%D in (.) do call :processFolder "%%~fD"
:: Cleanup and exit
subst y: /d
subst z: /d
exit /b
:processFolder
:: Count the files
for /f %%N in ('dir /a-d /b %1 2^>nul^|find /c /v ""') do set "cnt=%%N"
:: Nothing to do if folder is empty
if %cnt% equ 0 exit /b
:: Select a random number within the range
set /a N=%random% %% cnt + 1
:: copy the Nth file
for /f "delims=: tokens=2" %%F in (
'dir /a-d /b %1^|findstr /n .^|findstr "^%N%:"'
) do copy "%%D\%%F" "z:%%~pnxD" >nul
exit /b
EDIT
I fixed an obscure bug in the above code. The original COPY line read as follows:
copy "%%~1\%%F" "z:%%~pnx1" >nul
That version fails if any of the folders within the source tree contain %D or %F in their name. This type of problem always exists within a FOR loop if you expand a variable with %var% or expand a :subroutine parameter with %1.
The problem is easily fixed by using %%D instead of %1. It is counter-intuitive, but FOR variables are global in scope as long as any FOR loop is currently active. The %%D is inaccessible throughout most of the :processFolder routine, but it is available within the FOR loops.
The "natural" way to process a directory tree is via a recursive subroutine; this method minimize the problems inherent to this process. As I said at this post: "You may write a recursive algorithm in Batch that gives you exact control of what you do in every nested subdirectory". I taken the code at this answer, that duplicate a tree, and slightly modified it in order to solve this problem.
#echo off
setlocal
set "Destination=H:\Temp"
set "FileFilter=*.ape"
rem Enter to source folder and process it
cd /D "%~dp1"
call :processFolder
goto :EOF
:processFolder
setlocal EnableDelayedExpansion
rem For each folder in this level
for /D %%a in (*) do (
rem Enter into it, process it and go back to original
cd "%%a"
set "Destination=%Destination%\%%a"
if not exist "!Destination!" md "!Destination!"
rem Get the files in this folder and copy a random one
set "n=0"
for %%b in (%FileFilter%) do (
set /A n+=1
set "file[!n!]=%%b"
)
if !n! gtr 0 (
set /A "rnd=!random! %% n + 1"
for %%i in (!rnd!) do copy "!file[%%i]!" "!Destination!"
)
call :processFolder
cd ..
)
exit /B
Here is anther approach using xcopy /L to walk through all files in the source directory, which does not actually copy anything due to /L but returns paths relative to the source directory. For explanation of the code see all the remarks:
#echo off
setlocal EnableExtensions DisableDelayedExpansion
rem Define source and destination directories here:
set "SOURCE=%dp~1"
set "DESTIN=H:\Temp"
rem Change to source directory:
cd /D "%SOURCE%"
rem Reset index number:
set /A "INDEX=0"
rem Walk through output of `xcopy /L`, which returns
rem all files in source directory as relative paths;
rem `find` filters out the summary line; `echo` appends one more line
rem with invalid path, just to process the last item as well:
for /F "delims=" %%F in ('
2^> nul xcopy /L /S /I /Y "." "%TEMP%" ^
^| find ".\" ^
^& echo^(C:\^^^|\^^^|
') do (
rem Store path to parent directory of current item:
set "CURRPATH=%%~dpF"
setlocal EnableDelayedExpansion
if !INDEX! EQU 0 (
rem First item, so build empty directory tree:
xcopy /T /E /Y "." "%DESTIN%"
endlocal
rem Set index and first array element, holding
rem all files present in the current directory:
set /A "INDEX=1"
set "ITEMS_1=%%F"
) else if "!CURRPATH!"=="!PREVPATH!" (
rem Previous parent directory equals current one,
rem so increment index and store current file:
set /A "INDEX+=1"
for %%I in (!INDEX!) do (
endlocal
set /A "INDEX=%%I"
set "ITEMS_%%I=%%F"
)
) else (
rem Current parent directory is not the previous one,
rem so generate random number from 1 to recent index
rem to select a file in the previous parent directory,
rem perform copying task, then reset index and store
rem the parent directory of the current (next) item:
set /A "INDEX=!RANDOM!%%!INDEX!+1"
for %%I in (!INDEX!) do (
xcopy /Y "!ITEMS_%%I!" "%DESTIN%\!ITEMS_%%I!"
endlocal
set /A "INDEX=1"
set "ITEMS_1=%%F"
)
)
rem Store path to parent directory of previous item:
set "PREVPATH=%%~dpF"
)
endlocal
exit /B
For this approach the destination directory can also be located within the source directory tree.
I have a folder containing a bunch of subfolders and files, but the structure is a bit inefficient. For example:
Root Folder
----EmptyFolder1
--------Folder1
------------SubFolder1
------------File1
------------File2
----EmptyFolder2
--------Folder2
------------SubFolder2
------------File3
------------File4
How can I move all of the Folders/SubFolders/Files up in the tree and eliminate all of the EmptyFolders so that it looks more like this:
Root Folder
----Folder1
--------SubFolder1
--------File1
--------File2
----Folder2
--------SubFolder2
--------File3
--------File4
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "sourcedir=U:\sourcedir\t h r e e"
FOR /f "delims=" %%a IN (
'dir/s/b/a-d "%sourcedir%"'
) DO (
CALL :movefile %%a
)
:loop
SET "zapped="
FOR /d /r "%sourcedir%" %%a IN (.) DO (
RD "%%a" >NUL 2>NUL
IF NOT EXIST "%%a" SET zapped=Y
)
IF DEFINED zapped GOTO loop
DIR /s/b/ad "%sourcedir%
GOTO :EOF
:movefile
SET "oldfn=%*"
SET "newfn=!oldfn:%sourcedir%\=!"
SET "newfn=%sourcedir%\%newfn:*\=%"
FOR %%r IN ("%newfn%") DO (
ECHO MD "%%~dpr"
ECHO MOVE "%oldfn%" "%newfn%"
)
GOTO :eof
You would need to change the setting of sourcedir to suit your circumstances.
caution test on a representative subtree first!
The required MD commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(MD to MD to actually create the directories. Append 2>nul to suppress error messages (eg. when the directory already exists)
The required MOVE commands are merely ECHOed for testing purposes. After you've verified that the commands are correct, change ECHO(MOVE to MOVE to actually move the files. Append >nul to suppress report messages (eg. 1 file moved)
The section after the :loop label deletes any empty directories in the subtree. Of necessity, the commands are executed not merely displayed.
Be very, very careful about file/directorynames that contain symbols with special meaning to cmd.
(I do not think there is an exact similar match for this question)
I need to make a batch file for Windows (XP and 7) that will:
install Python
check for any instance of a previous python folder in PATH (typically C:/Python2x, C:/Python3x,C:/Python2x/Scripts, C:/Python3x/Scripts)
remove all these folders from PATHs permanently
add C:/Python/Scripts and C:/Python permanently in PATH in System/Environment variables
Is this something fairly doable using batch scripts ? I have read in a previous question that I can use setx to set the variable permanently but I am struggling with the matching part.
#ECHO OFF
SETLOCAL
SET "newpython=C:\Python;C:\Python\Scripts"
SET "newpath="
:temploop
SET tempfile=%random%%random%%random%
IF EXIST "%temp%\%tempfile%*" GOTO temploop
SET "tempfile=%temp%\%tempfile%"
CALL :showpath >"%tempfile%"
SET "response=x"
FOR /f "delims=" %%p IN ('type "%tempfile%"') DO (
CALL :addsegment "%%p"
IF NOT DEFINED response DEL "%tempfile%"&GOTO :EOF
)
SET "newpath=%newpython%%newpath%
DEL "%tempfile%"
CALL :getresp "Apply new PATH=%newpath% [Y/N/Q]?"
IF /i "%response%"=="Y" ECHO SETX PATH "%newpath%"
GOTO :EOF
:addsegment
SET "segment=%~1"
IF /i "%segment%"=="%segment:python=%" SET response=N&GOTO nosdel
CALL :getresp "Delete %segment% from path [Y/N/Q]?"
:nosdel
IF /i "%response%"=="N" SET "newpath=%newpath%;%segment%"
GOTO :eof
:getresp
SET "response="
SET /p "response=%~1 "
IF /i "%response%"=="Y" GOTO :eof
IF /i "%response%"=="Q" SET "response="&GOTO :eof
IF /i NOT "%response%"=="N" ECHO Please respond Y N or Q to quit&GOTO getresp
GOTO :eof
:showpath
ECHO(%path:;=&ECHO(%
GOTO :eof
Installation of python - well, that's up to you. Don't know where it will be installed from, or to - yor job to find out.
Assuming the new directories are concatenated and separated by ; in variable newpython and noting that directory-segment separators are \ not / (which is used for switches) then the above:
establishes a temporary file
analyses the path, presenting each segment to be deleted or no
re-assembles the path and prefixes the newpython directories.
Asks whether to apply the changes.
I don't know which options you require for the setx, so the command is simply ECHOed. You'd need to remove the ECHO from the SETX line to activate the setting of the path variable.
Note also that SETX does not set the target variable in existing or the current CMD instances - only those created in the future.
#echo off
setlocal
setlocal enableDelayedExpansion
set "_PATH_=%PATH:;=";"%"
set "new_path="
for %%P in (%_PATH_%) do (
set "path_element=%%~P"
rem :: check if the path element contains "/Python"
if "!path_element!" equ "!path_element:/Python=!" (
set new_path="!path_element!";!new_path!
)
)
rem :: add the new directories to path
set new_path=%new_path%;C:/Python/Scripts;C:/Python;
endlocal & new_path=%new_path%
rem :: set the new path
set path=%new_path%
rem :: import the path to the registry
(
echo Windows Registry Editor Version 5.00
echo [HKEY_CURRENT_USER\Environment]
echo "PATH"=%new_path%
) >"%temp%\~path.reg"
REGEDIT /S "%temp%\~path.reg"
del "%temp%\~path.reg" /q /f
endlocal
endlocal
may not work if there are special symbols in the path like !
I have two folders that contain all the same files and subfolders, but the conents inside each file may have changed. I want to write a batch file that will search through each file and look for any differences. What's the best tool for what I want to do?
No need for a batch file. A single FC command can do what you want:
fc folder1\* folder2\*
You can be more specific for the file mask in the first folder if you want. For example folder1\*.txt.
The command will report on files that exist in folder1 but are missing in folder2. Extra files in folder2 are simply ignored.
There are a number of options to the FC command. Enter HELP FC or FC /? from the command prompt to get more information.
EDIT
Extending the solution to support subfolders is a bit tricky. It is easy to iterate the folder hierarchy for a given root using FOR /R. The problem is getting the relative paths so that the hierarchy can be applied to another root.
The simplest solution is to use FORFILES instead, since it directly supports relative paths. but FORFILES is... S L O W :/
At this point, a batch file makes sense:
#echo off
setlocal
set "folder1=c:\path\To\Folder1\Root"
set "folder2=d:\path\To\Folder2\Root"
set "fileMask=*"
for /f "delims=" %%F in (
'echo "."^&forfiles /s /p "%folder1%" /m "%fileMask%" /c "cmd /c if #isdir==TRUE echo #relpath"'
) do fc "%folder1%\%%~F\%fileMask%" "%folder2%\%%~F\*"
Here is another way to accomplish the task. Set the variables Folder1 and Folder2 to the full path of the folders you want to compare and run the batch file.
Output:
Dup – the file exists in both folders and are identical.
Dif - the file exists in both folders but the content of the files are different.
New – The file exists in one folder but not the other.
#Echo Off
SetLocal EnableDelayedExpansion
Set "Folder1=%UserProfile%\Desktop\Test Folder1"
Set "Folder2=%UserProfile%\Desktop\Test Folder2"
For /R "%Folder1%" %%x In (*.*) Do (
Set "FullPath=%%x"
Set "RelPath=!FullPath:%Folder1%=!"
If Exist "%Folder2%!RelPath!" (
>Nul 2>&1 FC /b "%Folder1%!RelPath!" "%Folder2%!RelPath!" && (
Echo Dup - %%x
)||(
Echo Dif - %%x
)
) Else (
Echo New - %%x
)
)
For /R "%Folder2%" %%x In (*.*) Do (
Set "FullPath=%%x"
Set "RelPath=!FullPath:%Folder2%=!"
If Not Exist "%Folder1%!RelPath!" (
Echo New - %%x
)
)
I'm having better luck with the following batch file. The path names have to completely match though; so only the Drive Letter differs.
Mapping a shared network folder may make this easier. I can't say for sure for your case.
'---
setlocal
set "folder1=D:\User\Public"
set "Drv2=E:"
set "fileMask=*"
setlocal
set "folder1=<DrvLttr>:\<Folder>\<SubFolder>"
set "Drv2=<DrvLttr2>:"
set "LogFile=<SystemDrv>\User\<UserName>\<LogFileName>"
ECHO "the '>' may OVER WRITE or make a ~NEW~ File for the results" > "%LogFile%"
ECHO " '>>' adds to the end of the file >> "%LogFile%"
FOR /R %folder1% %%A in ( *.* ) DO FC "%%A" "%Drv2%%%~pnxA" 1>> "%LogFile%" 2>&1
If you wish to note what is going to be the command for testing, you can try inserting ECHO after DO. You can drop the 1>>... stuff to see the result on screen, instead of having to open the output file.
I modified a batch file I wrote for a CD process that should meet your need. It
takes 2 directory trees and compares each file in each tree
creates a list of the file differences (named File_differences.txt in the output folder)
and creates a folder with a diff file for each non-matching object
If both directory structures are under the same parent, then all you need to do is update the first 8 variables in the script. If the directory trees have different parents, then read through the scripts comments. Particularly lines 14, 38 and 46.
:: This script compares the contents of 2 trees
:: set a workspace location for the script outside of the trees being reviewed
set home=D:\path\to\batch_file_home
set Input=D:\path\to\batch_file_home\Input_Files
set Resource=D:\path\to\batch_file_home\Resource_Files
set Output=D:\path\to\where your want to view your\Output_Files
set environment=D:\path\to\parent directory containing the different tree structures (if they do not share a parent, then make this the drive)
:: the next 3 lines are only needed if you want to predefine multiple directories for comparison
set Prod=production
set QA=test
set Dev=branches\dev
:: If you remove the 3 lines above, then you need to replace the 2 below variables with values
:: If the trees are not under the same parent, then include the full path for Tree A and B below
set Tree_A=%Prod%
set Tree_B=%QA%
:: if you already have an object list, place it in the Input folder and remove lines 24 through 35 of this script
set Object_List=Object_List_minus_Direcotries.txt
set Output_File=File_differences.txt
if exist %Output%\%Output_File% del %Output%\%Output_File%
if exist %Output%\Differences\*.txt del %Output%\Differences\*.txt
if exist %Resource%\* del /q %Resource%\*
:: since you state the objects in both trees are always the same, I have not included a comparison to verify the 2 trees match
:: this section identifies the contents of Tree A
cd %environment%\%Tree_A%
dir /b /s > %Resource%\Complete_Tree_A_Object_List.txt
:: Next, remove the objects that are directories
setlocal enabledelayedexpansion
for /f "tokens=*" %%p in (%Resource%\Complete_Tree_A_Object_List.txt) do (
dir /a:d /b %%p 2>nul >nul && (set object_type=folder) || (set object_type=file)
echo !object_type!
if !object_type!==file echo %%p >> %Resource%\%Object_List%
)
:: in the object list, remove the parent tree from the path
:: if the Trees are not under the same parent, then comment out the below 6 lines
powershell -command "(Get-Content %Resource%\%Object_List%) -replace '\\','/' | set-content %Resource%\%Object_List%"
powershell -command "(get-content %Resource%\%Object_List%) | Foreach {$_.TrimEnd()} | Set-Content %Resource%\%Object_List%"
set remove_parent_prefix=%environment%\%Tree_A%
set remove_parent_prefix=%remove_parent_prefix:\=/%
powershell -command "(Get-Content %Resource%\%Object_List%) -replace '%remove_parent_prefix%/','' | set-content %Resource%\%Object_List%"
powershell -command "(Get-Content %Resource%\%Object_List%) -replace '/','\' | set-content %Resource%\%Object_List%"
:: the below loop assumes both Trees are under the same parent. If this is not the case, replace the cd %environment% line with cd %home%
:: when the Trees are not under the same parent, set home to the root location, example cd D:\
setlocal enabledelayedexpansion
for /f "tokens=*" %%x in (%Resource%\%Object_List%) do (
set Diff_File=%%x
set Diff_File=!Diff_File:\=-!
cd %environment%
fc %Tree_A%\%%x %Tree_B%\%%x > "%Output%\Differences\!Diff_File!-%Output_File%"
for %%a in ("%Output%\Differences\!Diff_File!-%Output_File%") do for /f %%b in ('find /c /v "" ^< "%%a" ') do if %%b LSS 3 del "%%a"
for %%R in ("%Output%\Differences\!Diff_File!-%Output_File%") do if not %%~zR lss 1 (
echo %%x >> %Output%\%Output_File%
)
for %%R in ("%Output%\Differences\!Diff_File!-%Output_File%") do if %%~zR lss 1 (
del "%Output%\Differences\!Diff_File!-%Output_File%"
)
)
endlocal
:: Clean up Resources. If you want to review the temp files used to create the report, comment out the below line
if exist %Resource%\* del /q %Resource%\*