Windows batch file to find duplicates in a tree - windows

I need a batch file ( Windows CMD is the interpreter, a .bat ) to do this type of task:
1) Search through a folder and its subfolders
2) Find files with the same filename and extension ( aka duplicates )
3) Check if they have the same size
4) If same name + same size, echo all the files except the first one ( practically I need to delete all except one copy )
Thanks for any type of help
This is only an initial script, just for check the files, in a folder and its subfolders, and their size:
#Echo off
Setlocal EnableDelayedExpansion
Set Dir=C:\NewFolder
For /r "%Dir%" %%i in (*) do (
Set FileName=%%~nxi
Set FullPath=%%i
Set Size=%%~zi
Echo "!FullPath!" - SIZE: !Size!
)
Echo.
Pause

This script does what you ask. Just set the ROOT variable at the top to point to the root of your tree.
#echo off
setlocal disableDelayedExpansion
set root="c:\test"
set "prevTest=none"
set "prevFile=none"
for /f "tokens=1-3 delims=:" %%A in (
'"(for /r "%root%" %%F in (*) do #echo %%~znxF:%%~fF:)|sort"'
) do (
set "currTest=%%A"
set "currFile=%%B:%%C"
setlocal enableDelayedExpansion
if !currTest! equ !prevTest! echo "!currFile!"
endlocal
set "prevTest=%%A"
)
But you can make the test more precise by using FC to compare the contents of the files. Also, you can incorporate the DEL command directly in the script. The script below prints out the commands that would delete the duplicate files. Remove the ECHO before the DEL command when you are ready to actually delete the files.
#echo off
setlocal disableDelayedExpansion
set root="c:\test"
set "prevTest=none"
set "prevFile=none"
for /f "tokens=1-3 delims=:" %%A in (
'"(for /r "%root%" %%F in (*) do #echo %%~znxF:%%~fF:)|sort"'
) do (
set "currTest=%%A"
set "currFile=%%B:%%C"
setlocal enableDelayedExpansion
set "match="
if !currTest! equ !prevTest! fc /b "!prevFile!" "!currFile!" >nul && set match=1
if defined match (
echo del "!currFile!"
endlocal
) else (
endlocal
set "prevTest=%%A"
set "prevFile=%%B:%%C"
)
)
Both sets of code may seem overly complicated, but it is only because I have structured the code to be robust and avoid problems that can plague simple solutions. For example, ! in file names can cause problems with FOR variables if delayed expansion is enabled, and = in file name causes a problem with npocmoka's solution.

#echo off
setlocal
for /f "tokens=1 delims==" %%# in ('set _') do (
set "%%#="
)
for /r %%a in (*.*) do (
if not defined _%%~nxa%%~za (
set "_%%~nxa%%~za=%%~fa"
) else (
echo %%~fa
)
)
endlocal

Related

decrement of a number in filename with cmd on windows

I have a bunch of files:
CAR_003.dat
CAR_003.obj
CAR_004_prev0.png
CAR_004_prev1.png
CAR_004_tex0.tga
I need to rename them to:
CAR_002.dat
CAR_002.obj
CAR_003_prev0.png
CAR_003_prev1.png
CAR_003_tex0.tga
how I can do this with cmd on windows in a batch?
You need to execute the following batch in the directory you want.
It will rename everything in the tree.
#ECHO off
SETLOCAL EnableDelayedExpansion
FOR /R %%f in (CAR*) DO (
FOR /F "usebackq tokens=1,2,* delims=_" %%m in ('%%~nxf') do (
SET filePath=%%~dpf
SET filePrefix=%%m_
SET /a fileNumber=1%%~nn-1001
SET fileNumber=000!fileNumber!
SET fileNumber=!fileNumber:~-3!
SET filePosfix=%%~no
SET fileExt=%%~xf
IF [!filePosfix!] EQU [] (
SET fileRest=%%~xf
) ELSE (
SET fileRest=_!filePosfix!!fileExt!
)
REM ECHO %%f
REM ECHO !filePath!!filePrefix!!fileNumber!!fileRest!
REM ECHO ~~~~~~~
MOVE "%%f" "!filePath!!filePrefix!!fileNumber!!fileRest!"
)
)

Use a Batch File to list files and allow the user to select which file to copy into a new destination

I am a newbie to Windows Scripting.
I am trying to list some txt files in several sub directories & want to copy a user selected file to a new destination. Please note that the file name is unique in different locations.
I got the first part to work (Listing out the files & locations) using the following script, but I am unable to copy the selected file to the new location.
#ECHO OFF
SET index=1
SETLOCAL ENABLEDELAYEDEXPANSION
SET FFPath="C:\Scripts - Backup Server\DKXpress_bkp"
SET NewPath=C:\DKServer
ECHO Recursively searching %FFPath%
echo.
FOR /F "delims=" %%f in ('DIR %FFPath%\*.txt /a:-d /s /b') DO (
SET file!index!=%%f
ECHO !index! - %%f
SET /A index=!index!+1
)
SETLOCAL DISABLEDELAYEDEXPANSION
SET /P selection="select file by number:"
SET file%selection% >nul 2>&1
IF ERRORLEVEL 1 (
ECHO invalid number selected
EXIT /B 1
)
SET NewFile=file%selection%
ECHO Copying %NewFile% to %NewPath%
ECHO.
COPY /Y "%NewFile%" "%NewPath%"
ECHO.
PAUSE
I think I am doing this part wrong
SET NewFile=file%selection%
Thank you all in advance
You don't need to set an index variable or delayed expansion, if you let Find do the work for you:
#Echo Off
Set "FFPath=C:\Scripts - Backup Server\DKXpress_bkp"
Set "NewPath=C:\DKServer"
Echo Recursively searching %FFPath%
Echo=
For /F "Delims==" %%A In ('"Set File[ 2>Nul"') Do Set "%%A="
For /F "Tokens=1* Delims=]" %%A In (
'"Dir /B/S/A-D-S-L "%FFPath%\*.txt" 2>Nul|Find /N /V """') Do (
Echo %%A] %%B
Set "File%%A]=%%B"
)
Echo=
Set /P "#=Select file by number: "
Echo=
For /F "Tokens=1* Delims==" %%A In ('"Set File[%#%] 2>Nul"') Do (
Echo Copying %%B to %NewPath%&Echo=
Copy /Y "%%B" "%NewPath%"
GoTo :End
)
Echo Invalid number selected
:End
Echo=
Pause
You need to use delayed expansion to get the file name assigned to the variable correctly.
SET NewFile=!file%selection%!
Remove the setlocal to disable delayed expansion.
You can try something like that :
#ECHO OFF
:Main
cls
SET index=1
SETLOCAL ENABLEDELAYEDEXPANSION
SET FFPath="C:\Scripts - Backup Server\DKXpress_bkp"
SET "NewPath=C:\DKServer"
ECHO Recursively searching %FFPath%
echo.
FOR /F "delims=" %%f in ('DIR %FFPath%\*.txt /a:-d /s /b') DO (
SET filepath[!index!]=%%f
ECHO [!index!] - %%~nxf - %%f
SET /A index=!index!+1
)
echo(
echo select file by number :
set /p Input=""
For /L %%i in (1,1,%index%) Do (
If "%INPUT%" EQU "%%i" (
ECHO Copying "!filepath[%%i]!" to "!NewPath!"
COPY /Y "!filepath[%%i]!" "!NewPath!"
)
)
echo Copying another file ? (Y = Yes or N = No) ?
set /p input2=""
If /I "!input2!"=="Y" (
goto :Main
) else (
goto :eof
)

Scraping all file names with their creation and modification date from a directory

I am running this bat script, fileNames.bat > print.txt, to get all file Names.
#echo off
setlocal disableDelayedExpansion
:: Load the file path "array"
for /f "tokens=1* delims=:" %%A in ('dir /s /b^|findstr /n "^"') do (
set "file.%%A=%%B"
set "file.count=%%A"
)
:: Access the values
setlocal enableDelayedExpansion
for /l %%N in (1 1 %file.count%) do echo !file.%%N!
However, I would also like to get in the output the creation and modification date for each file path.
My current output looks like that:
C:\Programs\FirefoxPortable\fileNames.bat
C:\Programs\FirefoxPortable\Firefox
C:\Programs\FirefoxPortable\print.txt
C:\Programs\FirefoxPortable\Firefox\App
C:\Programs\FirefoxPortable\Firefox\Data
Any suggestions, how to get the creation and modification date next to each file path?
I appreciate your replies!
Following .bat script produces a csv-like output with | vertical line delimited values of next pattern:
type|creation datetime|modification datetime|full path|
Delayed expansion kept disabled as there are file names containig ! exclamation mark(s): see the call set trick in Variables: extract part of a variable (substring) and in CALLing internal commands.
Note that retrieving creation date and time discriminates between folders and files.
%%G tokenization could vary for another regional settings!
#ECHO OFF
#SETLOCAL enableextensions disableDelayedExpansion
:: Set the working directory and store the previous folder/path
pushd d:\test
:: Load the file path "array"
set "file.count=0"
for /f "tokens=1* delims=:" %%A in ('dir /s /b^|findstr /n "^"') do (
set "file.%%A=%%B"
set "file.count=%%A"
)
:: Restore path/folder most recently stored by the PUSHD command
popd
:: Access the values (keep )
set /A "ii=0"
:loop
if %ii% GEQ %file.count% goto :loopend
set /A "ii+=1"
call set "file.curr=%%file.%ii%%%"
if exist "%file.curr%\" (
rem folder
for %%g in ("%file.curr%") do (
set "defined="
for /F "skip=5 tokens=1,2" %%G in ('
dir /-c /a:d /t:C "%file.curr%"
') do (
if not defined defined (
echo FLDR^|%%G %%H^|%%~tg^|%file.curr%^|
set "defined=%%~tg %%G %%H"
)
)
)
) else (
rem file
for %%g in ("%file.curr%") do (
set "defined="
for /F "skip=5 tokens=1,2" %%G in ('
dir /-c /a:-d /t:C "%file.curr%"
') do (
if not defined defined (
echo FILE^|%%G %%H^|%%~tg^|%file.curr%^|
set "defined=%%~tg %%G %%H"
)
)
)
)
goto :loop
:loopend
ENDLOCAL
goto :eof
My current output looks as follows (reduced to reasonable size):
==>D:\bat\SO\31824138.bat
FILE|26.07.2015 11:02|26.07.2015 11:02|d:\test\File N.txt|
FLDR|24.07.2015 20:21|05.08.2015 18:44|d:\test\set|
FILE|24.07.2015 20:23|24.07.2015 20:23|d:\test\set\hklm.txt|
FILE|24.07.2015 20:26|24.07.2015 20:30|d:\test\set\regs.txt|
FILE|05.08.2015 18:44|05.08.2015 18:45|d:\test\set\t!exclam!t.txt|
==>

How to create a unique output filename for Windows Script?

I am trying to create a windows script that should generate this kind of filename everytime I run it: filename1, filename2, filename3 and so on. Here is what I have so far:
(
#echo off
wmic logicaldisk get size,freespace,caption
) > disk.txt
I hope you can help me. Thanks!!
:: make a tempfile
:maketemp
SET "tempfile=%temp%\%random%"
IF EXIST "%tempfile%*" (GOTO maketemp) ELSE (ECHO.>"%tempfile%a")
You now have any number of filenames available.
%tempfile%a exists and is empty, but %tempfile%anythingelse should be available for use.
#ECHO OFF
SETLOCAL
SET "basename=filename"
SET /a outname=0
:genloop
SET /a outname+=1
IF EXIST "%basename% %outname%.txt" GOTO genloop
SET "outname=%basename% %outname%.txt"
ECHO %outname%
GOTO :EOF
Ah - increment the destination filename on each run. This should do that. It's not actually creating a file - you'd need to create the file %outname% each time to have it increment...
(the space between %basename% and %outname% is optional, of course - omit it if desired.)
edited to include .txt
This will give you up to 1000 filenames but you can go higher, up to 2 Billion, but the higher you go the longer the delay will be before it picks a filename.
#echo off
for /L %%a in (1,1,1000) do if not defined filename if not exist "filename%%a.txt" set "filename=filename%%a.txt"
(
wmic logicaldisk get size,freespace,caption
) > "%filename%"
#echo off
setlocal enableextensions
call :getNextFilename "filename*.txt" nextFilename
echo %nextFilename%
echo test > "%nextFilename%"
call :getNextFilename "%cd%\filename*.txt" nextFilename
echo %nextFilename%
echo test > "%nextFilename%"
endlocal
exit /b
:getNextFilename whatToSearch returnVariable
setlocal enableextensions enabledelayedexpansion
for /f %%a in ("$\%~1"
) do for /f "tokens=1,* delims=*" %%b in ("%%~nxa"
) do ( set "left=%%b" & set "right=%%c" )
set "max=0"
for %%a in ("%~1"
) do for /f "tokens=1 delims=%left%%right% " %%b in ("%%~nxa"
) do for /f "tokens=* delims=0 " %%c in ("0%%~b"
) do if %%~c geq !max! set /a "max=%%c+1"
endlocal & set "%~2=%~dp1%left%%max%%right%" & exit /b
This should find the next file in sequence independently of the existence of holes in the numeration of the files. A path can be included or omitted. The * will be used as the placeholder for the numeration. BUT this will not work if files or included paths have "problematic" characters.
If the date/time of creation of the file can be considered, then this version can be optimized as
:getNextFilename whatToSearch returnVariable
setlocal enableextensions disabledelayedexpansion
for /f %%a in ("$\%~1"
) do for /f "tokens=1,* delims=*?" %%b in ("%%~nxa"
) do ( set "left=%%b" & set "right=%%c" )
set "max=0"
for /f "delims=" %%a in ('dir /tc /o-d /b "%~1" 2^>nul'
) do for /f "tokens=1 delims=%left%%right% " %%b in ("%%~nxa"
) do for /f "tokens=* delims=0 " %%c in ("0%%~b"
) do set /a "max=%%c+1" & goto done
:done
endlocal & set "%~2=%~dp1%left%%max%%right%" & exit /b
that will take the latest created instance of the file set.
I finally figured out where to put the .txt extension. This is from #Magoo's answer but I wanted the file to be a text file so I placed the .txt twice in order for it to work properly.
#ECHO OFF
SETLOCAL
SET "basename=DISK-OUT"
SET /a outname=0
:genloop
SET /a outname+=1
IF EXIST "%basename% %outname%.txt" GOTO genloop
SET "outname=%basename% %outname%.txt"
(
wmic logicaldisk get size,freespace,caption
) > "%outname%"
GOTO :EOF

for command is executed only for the first value when a label is inside

I have the script
for /f "delims=" %%i in ('dir "%folder%*.txt" /b /s') do (
set s=%%i
set s=!s:%folder%=!
set new_s=!s:\=!
if "x!new_s!" NEQ "x!s!" (
:ProcessListSource
For /f "tokens=1* delims=\" %%A in ("!s!") do (
if "%%A" NEQ "" (
if "!Folder1!" NEQ "" (
Set Folder1=!Folder1!\!Name!
)else (
Set Folder1=!Name!
)
Set Name=%%A
)
if "%%B" NEQ "" (
set s=%%B
goto :ProcessListSource
)
)
echo Folder is: !Folder1!
echo Name is: !Name!
echo ---------------------
) else (
echo Not a folder !s!
)
)
but it does not work as I would have expected:
The first for is executed only once and also the last echo is printed on the screen.
Given a folder I need the files from subfolders without the given folder and than split them into the folder and file
Ex: folder=C:\test
The for would give me the file C:\test\test1\test2\t.txt
And I need test1\test2 and t.txt
GOTO breaks your FOR /F \ IF context and they can be executed only once.
More simple example:
#echo off
for /l %%S in (1=1=5) do (
echo %%S
goto :inner_label
rem
:inner_label
rem
)
This will print only 1 . Do you really need the GOTO here?
When the parser reads your code, all the code inside your for loop is "considered" as only one command that is readed, parsed and executed. As stated in the npocmaka answer, any goto call takes you out of this "line" of code, ending the process of the for loop.
This is a alternative. Use pushd + xcopy /l /s commands to generate a list of the relative paths of the files.
#echo off
setlocal enableextensions disabledelayedexpansion
set "folder=%cd%"
pushd "%folder%"
for /f "delims=" %%a in ('xcopy /l /s /y * "%temp%"^|findstr /vbr /c:"[0-9]"'
) do for /f "delims=: tokens=1,*" %%b in ("%%~a") do (
echo [%%c] [%%~nxa]
)
popd

Resources