Get size of a directory in 'MB' using batch file

I want to get the size of directory say C:\Temp in MB using batch file. I don't need sizes of child directories or files, but the size of directory itself.

I found an answer at How to list all folder with size via batch file

but it gives me size in bytes and that of sub folders. So my question is :

How to get the size of directory itself in MB ?


Solution 1:

You can do it with a hybrid script [Batch/Vbscript] like this one :

@echo off
set Folder="C:\temp"
echo The size of %Folder% is 
Call :GetSize %Folder%
pause
:GetSize
(
echo wscript.echo GetSize("%~1"^)
echo Function GetSize(MyFolder^)
echo    Set fso = CreateObject("Scripting.FileSystemObject"^)
echo    Set objFolder= fso.GetFolder(MyFolder^)  
echo    GetSize = FormatSize(objFolder.Size^)
echo End Function
echo '*******************************************************************
echo 'Function to format a number into typical size scales
echo Function FormatSize(iSize^)
echo    aLabel = Array("bytes", "KB", "MB", "GB", "TB"^)
echo    For i = 0 to 4
echo        If iSize ^> 1024 Then
echo            iSize = iSize / 1024
echo        Else
echo            Exit For
echo        End If
echo    Next
echo    FormatSize = Round(iSize,2^) ^& " " ^& aLabel(i^)
echo End Function
echo '*******************************************************************
)>%tmp%\Size.vbs
Cscript /NoLogo %tmp%\Size.vbs
Del %tmp%\Size.vbs
Exit /b

Edit : on 30/03/2016 @12:11

And this a nice trick

the Liviu's hack for embedding vbscode in batch without temp files

I just discovered from npocmaka thanks to him

@echo off
Set Folder="c:\temp"
@cScript.EXE //noLogo "%~f0?.WSF" %Folder%  //job:info %~nx0%*
pause
@exit /b 0
<job id="info">
<script language="VBScript">
wscript.echo GetSize(WScript.Arguments(0))
Function GetSize(MyFolder)
    Set fso = CreateObject("Scripting.FileSystemObject")
    Set objFolder= fso.GetFolder(MyFolder)  
    GetSize = FormatSize(objFolder.Size)
End Function
'*******************************************************************
'Function to format a number into typical size scales
Function FormatSize(iSize)
   aLabel = Array("bytes", "KB", "MB", "GB", "TB")
   For i = 0 to 4
        If iSize > 1024 Then
            iSize = iSize / 1024
        Else
            Exit For
        End If
   Next
   FormatSize = Round(iSize,2) & " " & aLabel(i)
End Function
'*******************************************************************
</script>
</job>

Solution 2:

@echo off
::robocopy "%~1" %TEMP% /S /L /BYTES /XJ /NFL /NDL /NJH
for /f "tokens=2 delims=: " %%a in ('
    robocopy "%~1" %TEMP% /S /L /BYTES /XJ /NFL /NDL /NJH ^| find "Bytes"
') do set "bytes=%%a"


::1048576

set "beginJS=mshta "javascript:close(new ActiveXObject('Scripting.FileSystemObject').GetStandardStream(1).Write("
set "endJS=));""




for /f %%N in (
  '%beginJS% %bytes%/1048576 %endJS%'
) do set mb=%%N
echo mb=%mb%

It takes one argument - the folder which size you want to calculate

Solution 3:

Here is a pure batch file solution (refer to the remarks in the code for some brief explanations):

@echo off
setlocal EnableExtensions DisableDelayedExpansion

rem Define constants here:
set /A DIVISOR=1024 & rem (1024 Bytes = 1 KBytes, 1024 KBytes = 1 MByte,...)
set "ROUNDUP=#" & rem (set to non-empty value to round up results)

rem Get size of directory given as command line argument:
for /F "tokens=2 delims=: " %%B in ('
    robocopy "%~1" "%~1" "*.*" /L /S /XJ /BYTES /NP /NFL /NDL /NJH ^| ^
        find /I "Bytes"
') do set "BYTES=%%B"
if not defined BYTES set "BYTES=0"
rem Display result in Bytes and divide it to get KBytes, MBytes, etc.:
call :DIVIDE %BYTES% 1 RESULT
echo( Bytes:    %RESULT%
call :DIVIDE %RESULT% %DIVISOR% RESULT REST
if defined ROUNDUP if 0%REST% GTR 0 set /A RESULT+=1
echo(KBytes:    %RESULT%
call :DIVIDE %RESULT% %DIVISOR% RESULT REST
if defined ROUNDUP if 0%REST% GTR 0 set /A RESULT+=1
echo(MBytes:    %RESULT%
call :DIVIDE %RESULT% %DIVISOR% RESULT REST
if defined ROUNDUP if 0%REST% GTR 0 set /A RESULT+=1
echo(GBytes:    %RESULT%

endlocal
exit /B


:DIVIDE val_dividend val_divisor [ref_result] [ref_remainder]
rem Divide a huge number exceeding the 32-bit limitation
rem by a 32-bit number in the range from 1 to 1000000000;
rem the result might also exceed the 32-bit limitation.
setlocal EnableDelayedExpansion
set "DIVIDEND=%~1"
set "DIVISOR=%~2"
set "QUOTIENT=%~3"
set "REMAINDER=%~4"

rem Check whether dividend and divisor are given:
if not defined DIVIDEND (
    >&2 echo(Too few arguments, dividend missing^^!
    exit /B 2
) else if not defined DIVISOR (
    >&2 echo(Too few arguments, divisor missing^^!
    exit /B 2
)
rem Check whether dividend is purely numeric:
for /F "tokens=* delims=0123456789" %%N in ("!DIVIDEND!") do (
    if not "%%N"=="" (
        >&2 echo(Dividend must be numeric^^!
        exit /B 2
    )
)
rem Convert divisor to numeric value without leading zeros:
for /F "tokens=* delims=0" %%N in ("%DIVISOR%") do set "DIVISOR=%%N"
set /A DIVISOR+=0
rem Check divisor against its range:
if %DIVISOR% LEQ 0 (
    >&2 echo(Divisor value must be positive^^!
    exit /B 1
) else if %DIVISOR% GTR 1000000000 (
    >&2 echo(Divisor value exceeds its limit^^!
    exit /B 1
)
set "COLL=" & set "NEXT=" & set /A INDEX=0
rem Do a division digit by digit as one would do it on paper:
:LOOP
if "!DIVIDEND:~%INDEX%,1!"=="" goto :CONT
set "NEXT=!NEXT!!DIVIDEND:~%INDEX%,1!"
rem Remove trailing zeros as such denote octal numbers:
for /F "tokens=* delims=0" %%N in ("!NEXT!") do set "NEXT=%%N"
set /A NEXT+=0
set /A PART=NEXT/DIVISOR
set "COLL=!COLL!!PART!"
set /A NEXT-=PART*DIVISOR
set /A INDEX+=1
goto :LOOP
:CONT
rem Remove trailing zeros as such denote octal numbers:
for /F "tokens=* delims=0" %%N in ("%COLL%") do set "COLL=%%N"
if not defined COLL set "COLL=0"
rem Set return variables or display result if none are given:
if defined QUOTIENT (
    if defined REMAINDER (
        endlocal
        set "%REMAINDER%=%NEXT%"
    ) else (
        endlocal
    )
    set "%QUOTIENT%=%COLL%"
) else (
    endlocal
    echo(%COLL%
)
exit /B

Basically, the size of the contents of the directory given as command line argument is gathered by robocopy, similar to npocmaka's approach; the resulting size in Bytes is stored in variable BYTES.

Afterwards, several subroutine calls are done, each to divide the result by 1024 in order to get the size in KBytes, then MBytes and GBytes.

The values resulting from the divisions are incremented by 1 if there is a remainder in order to round up the sizes (similar to Windows Explorer, which for example displays 1 KB for very small files). You can disable this feature by setting variable ROUNDUP to an empty string, which leads to rounding down.


Division Approach

Since the set /A command (and its division operator /) can handle (signed) 32-bit integers only, the subroutine :DIVIDE performs the actual division like one would do it on paper. The dividend (that is the number to be divided) is treated as a string, so the 32-bit range can be exceeded; the divisor (that is the number to divide by) however must not exceed it; rather it must be in the range from 1 to 1000000000. The loop at :LOOP consisting of goto :LOOP and a conditional goto :CONT constitutes a while-loop-like structure that iterates through all the decimal figures of the dividend and terminates after the last one has been reached.

The subroutine expects at least two arguments -- the values of the dividend and the divisor. It accepts even two more -- the name of a variable to hold the quotient (that is the division result) and another one to hold the remainder. If no optional arguments are given, the quotient is displayed on the console.