Is it possible to write a batch file that deletes all files in a directory for which the first n characters of the file's root name do not match the first n characters of any other filenames in that directory? For instance, suppose the directory contains the following:
Purcell_HenryA.txt
Purcell_HenryB.txt
Casaubon_IsaacA.txt
In this case, we would want to delete all files in the directory whose first 13 characters did not match the first 13 characters in any other files in the directory. (That is, we'd want to delete only Casaubon_IsaacA.txt.) I have tracked down scripts that delete all files with unique extensions in a directory, but don't know how to begin to write this script, and would therefore be grateful for any leads on the question.
This checks for root filenames of 14 characters and over - and if there is only 1 file with the same leading 13 characters then it will echo del. Remove the echo to make it perform the deletion.
#echo off
setlocal enabledelayedexpansion
for /f "delims=" %%a in ('dir /b /a-d') do (
set "part=%%~na"
if not "!part:~13,1!"=="" (
set "part=!part:~0,13!"
for /f "delims=" %%b in ('dir /b /a-d "!part!*.*" ^|find /c "!part!" ') do (
if %%b EQU 1 echo del "%%a"
)
)
)
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET target=u:\testdir
DIR /b /a-d %target%
echo====^^ names IN DIR ^^===
SET length=13
SET match=:
SET "candidate="
FOR /f "delims=" %%i IN ('dir /b/a-d "%target%\*"') DO (
SET filename=%%i
SET section=!filename:~0,%length%!
IF !section!==!match! (SET "candidate=") ELSE (
IF DEFINED candidate ECHO(DEL %target%\!candidate!
SET candidate=%%i
SET match=!section!
)
)
IF DEFINED candidate ECHO(DEL %target%\!candidate!
GOTO :EOF
Test result:
abc123_uniquename.txt
another_uniquename.txt
duplicate_name1234.txt
duplicate_name1235.txt
duplicate_name1236.txt
hello.txt
repeated__name1236.txt
repeated__name1235.txt
unique__name1235.txt
===^ names IN DIR ^===
DEL u:\testdir\abc123_uniquename.txt
DEL u:\testdir\another_uniquename.txt
DEL u:\testdir\hello.txt
DEL u:\testdir\unique__name1235.txt
If you are happy after testing, remove both ECHO( to activate the delete function.
For this code file name = name+extension:
#echo off &SETLOCAL enabledelayedexpansion
FOR %%a IN (*) DO (
SET "search=%%~a"
IF "!search:~13!" neq "" (
FOR /f "delims=[]" %%b IN ('dir /b /a-d /on "!search:~0,13!*" ^| find /n "!search:~0,13!"') DO SET found=%%b
IF !found! equ 1 ECHO DEL "%%~a"
)
)
And because I coose a very similar solution as foxidrive here is another one:
#echo off &SETLOCAL enabledelayedexpansion
FOR %%a IN (*) DO (
SET search=%%a
IF "!search:~13!" neq "" SET /a $!search:~0,13!+=1 2>nul
)
FOR /f "tokens=1*delims=$=" %%a IN ('set "$"') DO if %%b equ 1 echo del "%%~a*"
The way i'd go about this is as follows, i will explain the logic and i'll leave you to do the coding.
You will parse all the file names into variables, while increasing each time.
Then you will set a limit to the number of loops to go through. Then you will search the first 13 characters of the file name and if the number of lines is equal to 1 then delete it. After you will increase the variable by 1 and go through the loop, at the end of each loop it will check if it has reached the limit aka the number of files in the directory, if it has reached the limit, end the loop, otherwise continue.
hah, i finally decided to do it after a guy decided to use my idea i described into actual code, anyway this is way shorter and a lot faster than his, tested+verified to work:
#echo off & setlocal enabledelayedexpansion
set dir=directoryyouwanttosearchin
for /f "delims=" %%a in ('dir /A:a /b %dir%') do set /A name+=1 & set file!name!=%%a
:LOOP
set /A cnt+=1
for /f "delims=" %%a in ('dir /A:a /b %dir% ^| find /C /I "!file%cnt%:~0,13!"') do set lines=%%a
if %lines%==1 del %dir%\!file%cnt%! > nul
if %cnt% NEQ %name% Goto :LOOP
exit /b
That's 9 lines :).
Related
Thanks in advance for any help given.
After searching through all relative threads and google search I'm stumped on finding a solution to output a variable name for merging two PDF's.
So I have 100's of PDF's I need to combine (two at a time) in a folder c:/test
The files are set out like below
Company Name Invoice No 123456
Company Name Invoice No 123456 details
Now I have managed to move two files at a time to a different folder and merge them but can't seem to get the desrired output name I'm after which is to put a week ending date in front (or at the end, not fussed) of the first merged filename. Below is the code I have thus far which works but the output file name is blank but gets created.
Very new to batch scripting and would appreciate any help :)
#echo off
setlocal enableextensions enabledelayedexpansion
set pdftk=C:\Program Files (x86)\PDFtk Server\bin\pdftk.exe
set Source=C:\test
set Target=C:\test\test2
set num=2
set filenumber=1
for /F "tokens=1,2 delims=:" %%f in ('dir /b /a-d "%source%\*.pdf" ^| findstr /n "^" ') do (
if %%f leq %num% (
copy "%source%\%%g" "%target%" /y > nul
) else goto endCopy
)
:endCopy
endlocal
for /F "tokens=1,2 delims=:" %%f in ('dir /b /a-d "%target%\*.pdf" ^| findstr /n "^" ') do (
if %%f leq %filenumber% ( set file=%%~nA
)
)
pdftk *.pdf cat output we_19_9_2017_%file%.pdf
In endCopy you are trying to get the name of A whereas you are iterating with f. Use set file=%%~nf to set the name of file or set file=%%~ng for second file.
And move endlocal at the end to expand !file! at the end of script like this (note the !):
:endCopy
set "cmd=dir /b /a-d "%target%\*.pdf" ^| findstr /n "^""
for /F "tokens=1,2 delims=:" %%f in ('%cmd%') do if %%f leq %filenumber% set file=%%~nf
pdftk *.pdf cat output we_19_9_2017_!file!.pdf
endlocal
Read more about DelayedExpansion at: https://ss64.com/nt/delayedexpansion.html
The last command doesn't use the target folder for the input files and thus looks for the input files in the current folder, so either include the path or first change to the target path.
Also you set a path-variable for pdftk but don't use it.
If this path isn't included in the %path% it can't be find.
Try this (untested)
#echo off
setlocal enableextensions enabledelayedexpansion
set "pdftk=C:\Program Files (x86)\PDFtk Server\bin\pdftk.exe"
set "Source=C:\test"
set "Target=C:\test\test2"
set num=2
set filenumber=1
for /F "tokens=1,2 delims=:" %%f in (
'dir /b /a-d "%source%\*.pdf" ^| findstr /n "^" '
) do if %%f leq %num% (
copy "%source%\%%g" "%target%" /y > nul
) else goto endCopy
:endCopy
endlocal
for /F "tokens=1,2 delims=:" %%f in (
'dir /b /a-d "%target%\*.pdf" ^|findstr /n "^" '
) do if %%f leq %filenumber% set file=%%~nf
PushD "%Target%"
"%pdftk%" *.pdf cat output we_19_9_2017_%file%.pdf
PopD
I picked up the code below from another post. I believe it should pick up the current directory folder and include it in the renaming part of the process, however that doesn't seem to work for me.
#ECHO OFF
setlocal enabledelayedexpansion
PUSHD "%~1"
set inc=0
FOR /f "delims=" %%a in ('dir /b /a-d') DO (
set /a inc+=1
Echo Ren: "%%a" "%~n1!inc!%%~xa"
Ren "%%a" "%~n1!inc!%%~xa"
)
POPD
I have a .txt file that will be received into a folder each day named and time stamped. Example as below:
FileNameA_20170418153000.txt
Essentially I'd like to amend the code above to rename the file: filenam0001.txt and continue to update the sequence number (which works perfectly well).
i.e.
filenam0001.txt
filenam0002.txt
filenam0003.txt
Any help would be greatly appreciated.
you need to add the leading zeros manually (add some zeros, then cut the last x characters):
#echo off
setlocal EnableDelayedExpansion
set inc=0
for /l %%a in (1,1,50) do (
set /a inc+=1
set num=00000000!inc!
set num=!num:~-5!
echo !num!
)
I modified your code in order to insert the leading zeros in a simple way...
#ECHO OFF
setlocal enabledelayedexpansion
PUSHD "%~1"
set inc=10000
FOR /f "delims=" %%a in ('dir /b /a-d') DO (
set /a inc+=1
Echo Ren: "%%a" "%~n1_%%~na!inc:~1!%%~xa"
Ren "%%a" "%~n1_%%~na!inc:~1!%%~xa"
)
POPD
Hi I have a batch script to move x amount of files from one folder to another. The counter that count the files moved is not incrementing. The script is as follows
SETLOCAL ENABLEEXTENSIONS
SETLOCAL ENABLEDELAYEDEXPANSION
echo on
set DataMax=50
set Counter=1
set SrcMax=50
set DataLoc=Destination Folder
Set HoldLoc=Source Folder
set count=0
FOR /F %%a in ('DIR /B %DataLoc%\*.pst') do set /A count=count+1
if %count% GEQ %DataMax% (Goto Exit) else (GOTO FMove)
:FMove
Echo Gather Top 50 files
FOR /F "TOKENS=*" %%a IN ('dir /A-D /O-D /B %HoldLoc%\*.pst') DO (
if %Counter% LEQ %SrcMax% (
MOVE /y %HoldLoc%\%%a %DataLoc%\
SET /A Counter += 1
)
)
goto Exit
:Exit
exit
The Set /A Counter += 1 does not seem to work. Thanks in Advance for any assistance.
As you already have enabled delayed expansion try like:
FOR /F "TOKENS=*" %%a IN ('dir /A-D /O-D /B %HoldLoc%\*.pst') DO (
if !Counter! LEQ %SrcMax% (
MOVE /y %HoldLoc%\%%a %DataLoc%\
SET /A Counter=Counter+1
)
)
npockmaka has shown how to get your code to work under normal circumstances by using delayed expansion. However, it will fail if any files names contain the ! character (unlikely, but it could happen)
It is possible to make the code work without delayed expansion by intentionally dividing by zero when the maximum count is exceeded. The error message is hidden by redirecting to nul, and the || operator detects the error and conditionally executes the EXIT command.
I also streamlined the first loop to use FIND to quickly get the count, instead of iterating each file.
#echo off
setlocal
set /a count=0, SrcMax=DataMax=50
set "DataLoc=Destination Folder"
set "HoldLoc=Source Folder"
for /f %%N in (
'dir /b "%DataLoc%\*.pst"^|find /c /v ""'
) do if %%N geq %DataMax% exit /b
echo Gather Top 50 files
for /f "eol=: delims=" %%A in (
'dir /a-d /o-d /b "%HoldLoc%\*.pst"'
) do (
set /a "1/(SrcMax-count), count+=1" 2>nul || exit /b
move /y "%HoldLoc%\%%B" "%DataLoc%\"
)
Another option is to number each file via FINDSTR /N, and let FOR /F parse out the number and file name.
#echo off
setlocal
set /a SrcMax=DataMax=50
set "DataLoc=Destination Folder"
set "HoldLoc=Source Folder"
for /f %%N in (
'dir /b "%DataLoc%\*.pst"^|find /c /v ""'
) do if %%N geq %DataMax% exit /b
echo Gather Top 50 files
for /f "tokens=1* delims=:" %%A in (
'dir /a-d /o-d /b "%HoldLoc%\*.pst"^|findstr /n "^"'
) do (
if %%A gtr %SrcMax% exit /b
move /y "%HoldLoc%\%%B" "%DataLoc%"
)
There is one thing that concerns me in your logic.
If you already have 50 files in your destination, then you exit without doing anything. If you do not yet have 50 files, then you move up to 50 files from the source to the destination. If there are 49 files in the destination at the start, then there is the potential to end up with 99 files in the destination, assuming none of the moved file names match the existing files in the destination.
Is it possible to do this?
In a folder i have files with same initial names
Example:
Main folder
-Quest2323231.txt
Quest2343434.txt
Quest2343435.txt
Fund103.txt
Fund102.txt
I have a config file (abc.config) in which i have name of file on which i need to count and move them . If the count is more than 2 then i need to move them.
In this case for e g I need to find files which have name as 'Quest'
Appreciate you help on this.
#echo off
setlocal enableextensions
set "number="
for /f "tokens=1" %%a in (
'dir /a-d /-c "c:\mainfolder\quest*" 2^>nul^|findstr /b /c:" "'
) do if not defined number set "number=%%a"
if not defined number set "number=0"
echo %number%
Untested: This expects text in the first line of abc.config which is the file information such as Quest in the example and if there are more than 2 matching files in the source folder then it will echo a move command to move them to the target folder.
Change *%file%* to %file%* in two places if you want to match only the start of the filename.
Remove the echo to actually perform the move commands.
#echo off
set "source=c:\mainfolder"
set "target=d:\target\folder"
set /p "file=" < "abc.config"
for /f %%a in ('dir /b /a-d "%source%\*%file%*" ^|find /i "%file%" 2^>nul^|find /c /v "" ') do set "number=%%a"
if %number% GTR 2 for /f "delims=" %%a in ('dir /b /a-d "%source%\*%file%*" ^|find /i "%file%" ') do (
echo move "%source%\%%a" "%target%"
)
pause
I am trying to create a windows batch file that will scan a folder with many sub folders. Each sub folder can contain many files. I need the script to check if a sub folder contains over a certain number of files, and if it does move half of the files to a new folder with the same name but with a number at the end.
Example:
Main folder
-Subfolderone
-Subfoldertwo
-Subfolderthree
If Subfoldertwo contains over a certain number of files, lets say 1000, then half of the files within Subfoldertwo will be moved to Subfoldertwo(2), and so on for each sub folder.
Main folder
-Subfolderone
-Subfoldertwo
-Subfoldertwo(2)
-Subfolderthree
Any help would be much appreciated. Thank you.
#ECHO OFF
SETLOCAL
SET "sourcedir=c:\sourcedir"
SET limit=5
FOR /f "delims=" %%a IN ('dir /b /s /ad "%sourcedir%\*"') DO (
SET /a newnum=2
FOR /f %%c IN ('dir /b/a-d "%%~a" 2^>nul ^|find /c /v ""') DO IF %%c gtr %limit% CALL :process "%%a"
)
)
GOTO :EOF
:process
IF EXIST "%~1(%newnum%)\" SET /a newnum+=1&GOTO process
ECHO MD "%~1(%newnum%)"
FOR /f "skip=%limit%delims=" %%m IN ('dir /b /a-d "%~1"') DO ECHO MOVE "%~1\%%m" "%~1(%newnum%)\"
GOTO :eof
Simple enough. I've set the sourcedir to a constant for my testing and the limit to 5 for the same reason.
First build a list of the original diretory tree, then count the files in each directory. If that count is greater than the limit, process the directory.
In process, first find whether the proposed new directory already exists. If it does, keep incrementing the number 'til it doesn't.
Then list the filenames (only) from the original full-directoryname, skipping the first %limit% and for the remainder, move them to the new directoryname.
The required 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)
AND change ECHO MOVE to MOVE to actually move the files. Append >nul to suppress report messages (eg. 1 file moved)
Edit : revised for 'move half the files'
#ECHO OFF
SETLOCAL
SET "sourcedir=c:\sourcedir"
SET limit=5
FOR /f "delims=" %%a IN ('dir /b /s /ad "%sourcedir%\*"') DO (
SET /a newnum=2
FOR /f %%c IN ('dir /b/a-d "%%~a" 2^>nul ^|find /c /v ""') DO IF %%c gtr %limit% SET /a nmove=%%c / 2&CALL :process "%%a"
)
)
GOTO :EOF
:process
IF EXIST "%~1(%newnum%)\" SET /a newnum+=1&GOTO process
ECHO MD "%~1(%newnum%)"
FOR /f "skip=%nmove%delims=" %%m IN ('dir /b /a-d "%~1"') DO ECHO MOVE "%~1\%%m" "%~1(%newnum%)\"
GOTO :eof
(simply calculate half of the count into nmove then skip that number instead)
you might test this:
#ECHO OFF &SETLOCAL
set "StartFolder=X:\Main folder"
set /a MaxFiles=1000
cd /d "%StartFolder%"
:NewFolderCreated
set "NewFolderFlag="
for /f "delims=" %%a in ('dir /b /ad /on') do call:process "%StartFolder%\%%~a"
if defined NewFolderFlag (goto:NewFolderCreated) else goto:eof
:process
SETLOCAL
cd "%~1"
for /f %%b in ('dir /b /a-d 2^>nul^|find /c /v ""') do set /a FileCount=%%b
if %FileCount% leq %MaxFiles% exit /b
set /a MoveCount=FileCount-MaxFiles
set "CurrentFolder=%~n1"
set "NextPath=%StartFolder%\%CurrentFolder%(2)%~X1"
echo("%CurrentFolder%"|findstr /re ".*([0-9][0-9]*)\"^">nul||goto:moving
set "BasePath=%CurrentFolder:~0,-1%"
:loop
if not "%BasePath:~-1%"=="(" set "FolderNo=%BasePath:~-1%%FolderNo%"&set "BasePath=%BasePath:~0,-1%"&goto:loop
set /a FolderNo+=1
set "NextPath=%StartFolder%\%BasePath%%FolderNo%)%~X1"
:moving
echo(Moving %MoveCount% files from "%~1" to "%NextPath%".
md "%NextPath%" 2>nul &&set "NewFolderFlag=true"
for /f "skip=%MaxFiles%delims=" %%b in ('dir /b /a-d /o-n') do move "%~1\%%~b" "%NextPath%" >nul
endlocal &set "NewFolderFlag=%NewFolderFlag%"
exit /b