Fast(er) way to get folder size with batch script

女生的网名这么多〃 提交于 2019-12-01 20:09:24
user1016274

After some testing and comparing the performance of

dir /s
compact /s
and powershell GetChild-Item

I found that using robocopy is much faster. One additional advantage is that even very long paths do not cause an error (> 256 characters in path), for instance in deeply nested folders.
And if you prefer to not count data behind junctions that can easily be included with robocopy like this:

@echo off
pushd "%~1" || goto :EOF

for /f "tokens=2 delims= " %%a in ('
    robocopy "%CD%" "%TEMP%" /S /L /BYTES /XJ /NFL /NDL /NJH /R:0 ^| find "Bytes"
') do echo %CD%: %%a
popd

If you leave out the /BYTES option you'll get the size value formatted in MB or GB. One would have to print the dimension (k,m,g,t denoting kilo, mega, giga, tera) as well in this case, using another loop variable:

for /f "tokens=2-3 delims= " %%a in ('
    robocopy "%CD%" "%TEMP%" /S /L /XJ /NFL /NDL /NJH /R:0 ^| findstr "Bytes"
') do ( 
    set dim=%%b
    set "dim=!dim:k=KB!" & set "dim=!dim:m=MB!" & set "dim=!dim:g=GB!" & set "dim=!dim:t=TB!"    
    if !dim! EQU %%b set dim=B
    echo ^    %CD%: %%a !dim!
)  

%%b holds either the dimension letter or a numeric value. This is tested by substitution to avoid the 32bit limit of set /A.

You can try with (in the spirit of your second case)

@echo off
    setlocal enableextensions disabledelayedexpansion

    set "target=%~1"
    if not defined target set "target=%cd%"

    set "size=0"
    for /f "tokens=3,5" %%a in ('
        dir /a /s /w /-c "%target%"
        ^| findstr /b /l /c:"  "
    ') do if "%%b"=="" set "size=%%a"

    echo %size%

Since you are willing to use VBScript (based on your comment below your question), then you can simply use the FileSystemObject Folder object Size property. It reports the total size of all files within the folder, including files in all sub-folders (recursive).

The following simple JScript script prints out the size of the current folder:

var fso = new ActiveXObject("Scripting.FileSystemObject");
WScript.Echo(fso.GetFolder('.').Size);

I chose JScript instead of VBScript because it is simple to embed JScript within a batch script (though there are methods to do the same with VBScript).

Here is a simple hybrid script utility that reports the total size of any path you pass in as the first and only argument. The hybrid script makes it very convenient to call, since you don't have to specify CSCRIPT.

FolderSize.bat

@if (@X)==(@Y) @end /* Harmless hybrid line that begins a JScript comment

::FolderSize.bat  FolderPath
::
::  Print the total size of all files within FolderPath,
::  including all sub-folders, recursively.

::******** Batch Code *********
@echo off
cscript //nologo //e:jscript "%~f0" %1
exit /b

********** JScript Code *******/
var fso = new ActiveXObject("Scripting.FileSystemObject");
WScript.Echo(fso.GetFolder(WScript.Arguments.Unnamed(0)).Size);

The only limitation is you must have access to all folders (and files?) within the folder, otherwise it fails with an error message.

try this:

:foldersize
@echo off
pushd "%~1"

setlocal
set "_size="
for /f "tokens=1 delims=t" %%s in ('compact /s /q ^|find " total bytes"') do (
        set "_size=%%s"
)
set "_size=%_size:  =%"
set "_size=%_size: =%"
set "_size=%_size:.=%"
set "_size=%_size:,=%"
set "_size=%_size:      =%"
echo folder size is : %_size% bytes
endlocal
popd

it accepts one argument - the folder.The compact /s /q (/q is for reporting so no changes will be applied) produces less output and there's a chance to be faster than DIR .

EDIT: a little bit optimized variants (the one is the @MC MD's one - probably the faster).The idea is to skip FIND or FINDSTR usage as they are external programs and will make the scripts slower:

:foldersize
@echo off
pushd "%~1"

setlocal enableDelayedExpansion
set "last=#"
set "_size="
for /f "tokens=1 delims= " %%s in ('compact /s /q') do (
        set "_size=!last!"
        set "last=%%s"
)


set "_size=%_size:  =%"
set "_size=%_size: =%"
set "_size=%_size:.=%"
set "_size=%_size:,=%"
set "_size=%_size:      =%"
echo folder size is : %_size% bytes
endlocal
popd

AND

@echo off
:original script by MC ND
    setlocal enableextensions enableDelayedExpansion

    set "target=%~1"
    if not defined target set "target=%cd%"

    set "size=0"
    set "last=#"
    set "pre_last=#"
    rem set "pre_pre_last=#"
    for /f "tokens=3" %%a in ('
        dir /a:-d /s /w /-c "%target%"  
    ') do  (

        set "pre_last=!last!"
        set "last=%%a"


    )
    echo !pre_last!

I think that looping over each line of output of the compact or dir command is inefficient and can be avoided by filtering the interim result:

@echo off
REM dirsize.cmd 2015-05-29

pushd "%~1" || goto :EOF
setlocal
for /f "tokens=1-3*" %%A in ('compact /s /a /q ^| find "Datenbytes" ^| find /v "Auflistung"') do echo %CD%: %%A %%B %%C
popd

Changes:
- the script will terminate if the given path does not exist rather than scanning the current directory - compact /a is used to include hidden and system files as well - the complete output is piped into a find. This is where a locale dependent search string is needed, to filter out the summary line. In German it's "Datenbytes" but this may as well be included in a foldername. Thus, a second negative filter will suppress these. Again, locale dependent (but independence was not called for).
The advantage is that find will discard output lines faster than a shell loop with variable assignments. The cost of calling it is neglegible.

Please note that compact /q will not stop the compression action. It will only shorten the output. Not supplying any arguments in the call to compress will make it list only and not compact files/folders.

edit: Though these points are all valid IMHO, see my other answer for a much faster way.

If you're not opposed to using PowerShell, Here's a quick script you can use:

param([String]$path=".")
Get-ChildItem $path | Measure-Object -property length -sum
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!