I haven't learned powershell, but I did solve the problem with JScript.
It would be really simple if all you needed was the total folder size (including size of all subfolders, recursive), because the FileSystemObject folder.size property gives that value directly. But there are no properties to get the corresponding file and folder count, so you must write your own recursive code to get the counts.
folderSummary.js
var fso = new ActiveXObject("Scripting.FileSystemObject");
var maxDepth = parseInt(WScript.Arguments.Item(1));
var files;
procFolder( fso.GetFolder( WScript.Arguments.Item(0) ), 0, new Summary() );
function procFolder( folder, depth, parent ) {
var summary = new Summary();
for (files = new Enumerator(folder.Files); !files.atEnd(); files.moveNext()) {
summary.fileCnt++;
summary.size += files.item().size;
}
for (var folders = new Enumerator(folder.SubFolders); !folders.atEnd(); folders.moveNext()) {
summary.folderCnt++;
procFolder( folders.item(), depth+1, summary );
}
if (depth<=maxDepth) WScript.Echo( lpad( summary.folderCnt, ' ' ) + ' folders ' +
lpad( summary.fileCnt, ' ' ) + ' files ' +
lpad( summary.size, ' ') + ' bytes ' +
folder.Path
);
parent.size += summary.size;
parent.fileCnt += summary.fileCnt;
parent.folderCnt += summary.folderCnt;
}
function Summary() {
this.size = 0;
this.fileCnt = 0;
this.folderCnt = 0;
}
function lpad( val, pad ) {
if (!pad) pad='';
var rtn=val.toString();
return (rtn.length<pad.length) ? (pad+rtn).slice(-pad.length) : val;
}
Usage - It expects the root folder as the first argument, and the display recursion depth as the second argument. Here is an example for the current directory, 3 levels deep:
cscript //nologo folderSummary.js . 3
It is simple to package the JScript into a hybrid batch script to make it easier to call from the command line. The script below defaults the root to the current folder, and the depth to 0.
folderSummary.bat
@if (@X)==(@Y) @end /* Harmless hybrid line that begins a JScript comment
:: folderSummary.bat [rootPath] [recursionDepth]
::
:: default rootPath is current folder
:: default recursionDepth is 0
::
::************* Batch portion **********
@echo off
setlocal disableDelayedExpansion
set "root=%~1"
if "%root%" equ "" set "root=."
set "depth=%~2"
if "%depth%" equ "" set "depth=0"
cscript //nologo //E:JScript "%~f0" "%root%" "%depth%"
exit /b
************* JScript portion **********/
var fso = new ActiveXObject("Scripting.FileSystemObject");
var maxDepth = parseInt(WScript.Arguments.Item(1));
var files;
procFolder( fso.GetFolder( WScript.Arguments.Item(0) ), 0, new Summary() );
function procFolder( folder, depth, parent ) {
var summary = new Summary();
for (files = new Enumerator(folder.Files); !files.atEnd(); files.moveNext()) {
summary.fileCnt++;
summary.size += files.item().size;
}
for (var folders = new Enumerator(folder.SubFolders); !folders.atEnd(); folders.moveNext()) {
summary.folderCnt++;
procFolder( folders.item(), depth+1, summary );
}
if (depth<=maxDepth) WScript.Echo( lpad( summary.folderCnt, ' ' ) + ' folders ' +
lpad( summary.fileCnt, ' ' ) + ' files ' +
lpad( summary.size, ' ') + ' bytes ' +
folder.Path
);
parent.size += summary.size;
parent.fileCnt += summary.fileCnt;
parent.folderCnt += summary.folderCnt;
}
function Summary() {
this.size = 0;
this.fileCnt = 0;
this.folderCnt = 0;
}
function lpad( val, pad ) {
if (!pad) pad='';
var rtn=val.toString();
return (rtn.length<pad.length) ? (pad+rtn).slice(-pad.length) : val;
}
Sample usage:
rem root = Current folder, depth = 0
folderSummary
rem root = c:\test, depth = 3
folderSummary c:\test 3
The above can be fairly slow because they recursively iterate every file and folder within the root path. Here is a
much faster version that only iterates the folders - the total size is gotten from folder.Size, and file/folder collection item counts are gotten from the Count property. But, this fast version fails if it runs across a file/folder to which you do not have access.
fastFolderSummary.bat
@if (@X)==(@Y) @end /* Harmless hybrid line that begins a JScript comment
:: fastFolderSummary.bat [rootPath] [recursionDepth]
::
:: default rootPath is current folder
:: default recursionDepth is 0
::
::************* Batch portion **********
@echo off
setlocal disableDelayedExpansion
set "root=%~1"
if "%root%" equ "" set "root=."
set "depth=%~2"
if "%depth%" equ "" set "depth=0"
cscript //nologo //E:JScript "%~f0" "%root%" "%depth%"
exit /b
************* JScript portion **********/
var fso = new ActiveXObject("Scripting.FileSystemObject");
var maxDepth = parseInt(WScript.Arguments.Item(1));
procFolder( fso.GetFolder( WScript.Arguments.Item(0) ), 0, new Summary() );
function procFolder( folder, depth, parent ) {
var summary = new Summary();
summary.fileCnt += folder.Files.Count;
summary.folderCnt += folder.SubFolders.Count;
for (var folders = new Enumerator(folder.SubFolders); !folders.atEnd(); folders.moveNext()) {
procFolder( folders.item(), depth+1, summary );
}
if (depth<=maxDepth) WScript.Echo( lpad( summary.folderCnt, ' ' ) + ' folders ' +
lpad( summary.fileCnt, ' ' ) + ' files ' +
lpad( folder.size, ' ' ) + ' bytes ' +
folder.Path
);
parent.fileCnt += summary.fileCnt;
parent.folderCnt += summary.folderCnt;
}
function Summary() {
this.fileCnt = 0;
this.folderCnt = 0;
}
function lpad( val, pad ) {
if (!pad) pad='';
var rtn=val.toString();
return (rtn.length<pad.length) ? (pad+rtn).slice(-pad.length) : val;
}