Batch file: sorting folders and files alphabetically - batch-file

The batch file below recursively echos files and folders while adding some simple formatting, such as indenting to show the recursion depth, adding "/ " before folder names, "*" before certain files, and skipping folders named "Archive". It works great except that files and folders are sorted randomly, rather than alphabetically. How could this be changed to sort both files and folders alphabetically?
#echo off
setlocal disableDelayedExpansion
pushd %1
set "tab= "
set "indent="
call :run
exit /b
:run
REM echo the root folder name
for %%F in (.) do echo %%~fF
echo ------------------------------------------------------------------
set "folderBullet=\"
set "fileBullet=*"
:listFolder
setlocal
REM echo the files in the folder
for %%F in (*.txt *.pdf *.doc* *.xls*) do echo %indent%%fileBullet% %%F - %%~tF
REM loop through the folders
for /d %%F in (*) do (
REM skip "Archive" folder
if /i not "%%F"=="Archive" (
REM if in "Issued" folder change the file bullet
if /i "%%F"=="Issued" set "fileBullet= "
echo %indent%%folderBullet% %%F
pushd "%%F"
set "indent=%indent%%tab%"
call :listFolder
REM if leaving "Issued folder change fileBullet
if /i "%%F"=="Issued" set "fileBullet=*"
popd
))
exit /b

Very little change required. Convert FOR loops to FOR /F running sorted DIR commands. The /A-D option lists files only, and /AD lists directories only.
This version sorts files by name
#echo off
setlocal disableDelayedExpansion
pushd %1
set "tab= "
set "indent="
call :run
exit /b
:run
REM echo the root folder name
for %%F in (.) do echo %%~fF
echo ------------------------------------------------------------------
set "folderBullet=\"
set "fileBullet=*"
:listFolder
setlocal
REM echo the files in the folder
for /f "eol=: delims=" %%F in (
'dir /b /a-d /one *.txt *.pdf *.doc* *.xls* 2^>nul'
) do echo %indent%%fileBullet% %%F - %%~tF
REM loop through the folders
for /f "eol=: delims=" %%F in ('dir /b /ad /one 2^>nul') do (
REM skip "Archive" folder
if /i not "%%F"=="Archive" (
REM if in "Issued" folder change the file bullet
if /i "%%F"=="Issued" set "fileBullet= "
echo %indent%%folderBullet% %%F
pushd "%%F"
set "indent=%indent%%tab%"
call :listFolder
REM if leaving "Issued folder change fileBullet
if /i "%%F"=="Issued" set "fileBullet=*"
popd
))
exit /b
To sort by extension first, then by name, simply change /ONE to /OEN.

Try changing your for /d loop from
for /d %%F in (*) do
to
for /f "delims=" %%F in ('dir /b /o:n *.') do
and see whether that makes a difference. Actually, ordering by name is the default behavior for dir, so you could probably get away with
for /f "delims=" %%F in ('dir /b *.') do
If some of your directory names have dots in them, you'll need to change it a little.
for /f "delims=" %%F in ('dir /b') do (
rem Is this a directory?
if exist "%%F\" (
rem do your worst....
)
)

Related

How to delete only text files except few files

In a certain path I have some different kinds of file type. Eg., .txt, .bas, .cls, etc.
I need to delete only the text files in that path except few files.
For eg, if the path has a.txt, b.txt, c.txt, aa.bas, bb.cls, it should delete only a.txt. It should not delete b.txt and c.txt (Also it should not delete the other extension files).
To delete all ?.txt files in the root folder, excluding b.txt and c.txt
#echo off
for %%i in (?.txt) do (
if not "%%~nxi"=="c.txt" if not "%%~nxi"=="b.txt" echo del "%%i"
)
To do this in the root and subdirectories:
#echo off
for /R %%i in (?.txt) do (
if not "%%~nxi"=="c.txt" if not "%%~nxi"=="b.txt" echo del "%%i"
)
If the files are to be all *.txt files and not just single digit as per your example (add /R to recurse:
#echo off
for %%i in (*.txt) do (
if not "%%~nxi"=="c.txt" if not "%%~nxi"=="b.txt" echo del "%%i"
)
Similarly, but using findstr to only exclude:
#echo off
for /f %%i in ('dir /b /a-d ^|findstr /vi "b.txt" ^|findstr /vi "c.txt"') do (
echo del "%%i"
)
and to search only include:
#echo off
for /f %%i in ('dir /b /a-d ^|findstr /i "a.txt"') do (
echo del "%%i"
)
and to include and search subdirectories:
#echo off
for /f %%i in ('dir /b /s /a-d ^|findstr /i "a.txt"') do (
echo del "%%i"
)
On all of the above examples, remove echo to actually perform the delete, echo is used as a safety measure and will only display the del result to console.
Edit
Seeing as you specifically have a list of files (as per one of you comments) to exclude, you can use something like this. You have to create a file called exclusion.txt and add the files to exclude in list form:
b.txt
c.txt
file with space.txt
d.txt
Then create the batch file and add the code below. When ran, it will prompt for the file extention to filter on, where you can type an extension. i.e txt or simply press enter to perform a delete on all files, except the excluded ones. Just to be safe, I added an additional for loop to simply echo the files and prompt you if you are sure you want to delete the files.
#echo off
set cnt=0 & set excl= & set ext=
echo(
if not exist exclusion.txt echo You have not created an "exclusion.txt" file. & echo( & echo You need to create it first, then rerun the script & echo( & pause & goto :eof
echo Ensure you have listed all files to be excluded in "exclusion.txt" file
echo(
set /p "ext=Add File extention to search on (txt, pdf, etc), or press enter for all files: "
if not defined ext goto cont
if not "%ext:~0,1%"=="." set "ext=.%ext%"
set "ext=*%ext%"
:cont
setlocal enabledelayedexpansion
for /f "delims=" %%a in (exclusion.txt) do (
set /a cnt+=1
set "nlr!cnt!=%%a"
)
for /l %%i in (1,1,%cnt%) do (
if not defined excl (
set "excl=!nlr%%i!"
) else (
set "excl=!excl! !nlr%%i!"
)
)
echo(
echo WARNING: You are about to delete the following files!!
echo(
for /f "delims=" %%i in ('dir /b /a-d %ext% ^|findstr /VIE "%excl%"') do (
if /i not "%%i"=="exclusion.txt" if not "%%i"=="%~0" echo %%i
)
echo(
Choice /c YN /m "Are you sure you want to delete these files?"
if %errorlevel% equ 2 goto :eof
for /f "delims=" %%i in ('dir /b /a-d %ext% ^|findstr /VIE "%excl%"') do (
if /i not "%%i"=="exclusion.txt" if not "%%i"=="%~0" del %%i
)

Batch file for windows to rename multiple folders using a fixed part adding progressive number

I have many folders in a directory which I need to rename with a fixed base name and a progressive number starting from 1 to infinite.
Path of folders have space and base folder is D:\Programmi Installati.
Example of folders to rename:
log_1
log_2
log_04-01-2019 15-15-11,51
log_01-01-2019 8-22-14,19
log_27-12-2018 14-23-18,28
log_aaaa
log_bbbb
log_5
log_6
log_02-01-2019 6-21-17,34
log_03-01-2019 21-18-16,22
Example of wanted folder names:
log_1
log_2
log_3
log_4
log_5
log_6
log_7
log_8
log_9
log_10
log_11
log_12
The numbers of folder to rename can be large, but the structure is the same.
I tryed more batch file but all fail when there are some folders with the wanted name (example log_5 or log_1)
The order is not important it is important that all the folders starting with "log" are renamed with an incrental number.
Code already tryed without success
:: 1 code
#echo off
setlocal enabledelayedexpansion
set counter=
for /d %%a in ("D:\Programmi Installati\log_*") do (
set /a counter+=1
ren "%%~fa" "log_!counter!"
)
pause
:: 2 code
#echo off
setlocal EnableExtensions EnableDelayedExpansion
set "Counter=1"
for /F "delims=" %%I in ('dir "D:\Programmi Installati\log*" /AD /B /ON 2^>nul') do ren "D:\Programmi Installati\%%I" "log_!Counter!" & set /A Counter+=1
endlocal
pause
:: 3 code
#ECHO OFF
#setlocal enabledelayedexpansion
Rem | Folder Path & CD To Location
Set "Folder=D:\Programmi Installati\"
CD %Folder%
Rem | Get Raw File Name
Set "Number=1"
for /F "tokens=*" %%A in ('dir "log*" /S /b /AD') do (
Rem | Rename Folder || Raw Name - %%~n1
rename "%%~nA" "log_!Number!"
Rem | Add One To Number
set /a "number+=1"
)
Goto :EOF
PAUSE
The codes over works only if there is no desired directory name in the directory otherwise do not rename folders.
This batch works differently, it
skips folders with the proper naming scheme (so they keep the number)
increments a counter and fills in possible gaps
:: Q:\Test\2019\01\11\SO_54149437.cmd
#Echo off
Pushd "D:\Programmi Installati\" || (Echo couldn't change dir&pause&goto :eof)
set Cnt=0
for /f "delims=" %%A in (
'dir /B /AD log_* ^| findstr /iV "^log_[0-9][0-9]*$" '
) Do Call :RenInc "%%A"
PopD
Goto :Eof
:RenInc
Set /A Cnt+=1
if Exist "log_%Cnt%" goto :RenInc
Ren "%~1" "log_%Cnt%"
The resulting names (there are only eleven, not twelve)
log_1
log_10
log_11
log_2
log_3
log_4
log_5
log_6
log_7
log_8
log_9

File Retention Script

I have a directory that has a 10 sub-directories. Each of these holds different .bak files. I'm trying to create a script that will check to see if X number of files are there and if the number of files exceeds X, it deletes the oldest file. In other words, I want 20 iterations of a .bak file. When the 21st one shows up, I want the batch file to delete the oldest one.
Is this possible?
If so, can I create a single script that looks in all the sub-directories?
Thanks in advance.
Two options included. The first one will leave %maxFiles% bak files under each of the folders. The second one (windows Vista or later OS is required as robocopy is used to obtain the sorted list of files) will leave %maxFiles% in total
#echo off
setlocal enableextensions disabledelayedexpansion
set "rootFolder=%cd%"
set "maxFiles=20"
rem Option 1 - Keep %maxFiles% inside each of the subfolders
for /d /r "%rootFolder%" %%z in (*) do for /f "skip=%maxFiles% delims=" %%a in (
'dir /tc /o-d /a-d /b "%%~fz\*.bak" 2^>nul'
) do echo del "%%~fz\%%~nxa"
echo ------------------------------
rem Option 2 - Keep %maxFiles% in total under all the subfolders
for /f "skip=%maxFiles% tokens=2,*" %%a in ('
robocopy "%rootFolder%" "%rootFolder%" *.bak /l /nocopy /is /s /njh /njs /ndl /nc /ns /ts
^| findstr /v /r /e /i /c:"%rootFolder:\=\\%\\[^\\]*"
^| sort /r
') do echo del "%%b"
del commands are only echoed to console. If the output is correct, remove the echo command to remove the files
assumes that you want to check the number of files from the parent directory.Can be done also for each sub-directory.
#echo off
setlocal
set "max_number_files=20"
set "parrent_dir=c:\whatever_you_need"
set "extension=.bak"
pushd "%parrent_dir%"
set "count=0"
setlocal enableDelayedExpansion
for /f "delims=" %%a in ('dir /s /b /a:-d /o:-d /t:c *%extension%') do (
set /a count=count+1
if 1!count! GTR 1!max_number_files! (
rem --- remove the echo to activate deletion
echo del /q /f "%%~a"
)
)
popd
endlocal
endlocal
This will check each folder under d:\base\folder and if there are more than 20 *.bak files it will remove the oldest ones so only 20 *.bak file remain, in each folder.
Test it on some sample folders.
#echo off
for /d /r "d:\base\folder" %%a in (*) do (
pushd "%%a"
for /f "skip=20 delims=" %%b in ('dir /b /a-d /o:-d *.bak ') do del "%%b"
popd
)

Windows Command to identify folders with only one file

I need a command to run from the Windows CLI to identify any folders (or sub folders) that contain only one file. If the folder contains two files, it should not be included. In the end, I need to output this list to a text file. It should contain the full folder path.
Ex: OutputLog.txt
C:\fold1
C:\fold1\sub
C:\fold3
C:\fold4
This should work to identify folders with one file.
#echo off
for /d /r "d:\base\folder" %%a in (*) do (
dir /b /a-d "%%a" 2>nul |find /c /v "" |findstr "^1$" >nul && >>file.txt echo %%a
)
#echo off
setlocal EnableDelayedExpansion
(for /D /R %%a in (*) do (
set count=0
for %%b in ("%%a\*.*") do set /A count+=1
if !count! equ 1 echo %%a
)) > OutputLog.txt
#echo off
set "parentfolder=c:\test"
for /f "tokens=* delims=" %%F in ('dir /s /a:d /b "%parentfolder%"') do (
dir "%%F"|findstr /b "1 File(s)" >nul 2>&1 && echo %%F
)
This will list all subfolders with only one file in a parent folder .As it checks the string of the dir command output it should be altered if language settings/windows version provide different DIR command output.

batch script to recursively loop sub-directories and find missing files in 2 folders

I would like to compare recursively 2 folders and find missing files in them. I am using for loop to compare 2 folders but not able to search sub folders. Can somebody help?
Here is the code that I tried,
#echo off
if "%2" == "" GOTO Usage
cd /D %1
if errorlevel 1 goto usage
for %%x in (*.*) do if NOT exist %2\%%x echo missing %2\%%x
cd /D %2
for %%x in (*.*) do if NOT exist %1\%%x echo missing %1\%%x
goto end
:usage
echo Usage %0 dir1 dir2
echo where dir1 and dir2 are full paths
:end
try this, for explanation see comments in the code:
#ECHO OFF &SETLOCAL
SET "folder1=this"
SET "folder2=that"
REM delete variables
FOR /f "delims==" %%a IN ('set "$"') DO SET "%%a="
REM scanning folder1
FOR /r "%folder1%" %%a IN (*) DO SET "$%%~nxa=%%~a"
REM compare with folder2
FOR /r "%folder2%" %%a IN (*) DO (
IF NOT DEFINED $%%~nxa ECHO missing IN %folder1%: %%a
)
REM delete variables
FOR /f "delims==" %%a IN ('set "$"') DO SET "%%a="
REM scanning folder2
FOR /r "%folder2%" %%a IN (*) DO SET "$%%~nxa=%%~a"
REM compare with folder1
FOR /r "%folder1%" %%a IN (*) DO (
IF NOT DEFINED $%%~nxa ECHO missing IN %folder2%: %%a
)
ECHO Done.
#ECHO OFF
SETLOCAL
XCOPY /l /y /d "%~1\*" "%~2\*"|FIND "\"
XCOPY /l /y /d "%~2\*" "%~1\*"|FIND "\"
GOTO :EOF
Not strictly listing the files that are in one directory but not in the other; lists those and also those that exist in both but have a different timestamp.
Add /s to the XCOPY options to scan subdirectories also.

Resources