| """
sizes.cobra
The most effective technique to free up disk space on a drive is to look in the
directories that consume the most space. You may find entire directories that
can be deleted or excessively large files that are no longer needed. The first
time you do this, you might downsize the space consumed by a large factor with
only a few minutes of effort. Credit goes to the old "Dark Forest" application
on the old NeXTstep platform for introducing me to this approach.
This command line utility scans the current directory and its files for their
sizes, including all subdirectories. It then reports the total size and shows
the largest directories and files first. You can use this report to look for
large things to delete and rerun it as you progress. To run:
> cd \to\path\of\interest
> cobra \path\to\sizes.cobra
Unix users get a similar result with something like "du -k | sort -nr"
As an example, I ran `sizes` on a directory and it reported:
203658 directories and files read
180.30 seconds to read
21,297.34 MB
After spending 20 minutes cleaning up the biggest directories and files
that it listed, it then reported this:
186345 directories and files read
49.95 seconds to read
8,809.15 MB
"""
class FileNode implements IComparable
"""
One of these will be created for every directory and file, and put into a
list that can be sorted.
"""
test
a = FileNode('a', 100)
assert a.pathName == 'a'
assert a.numBytes == 100
assert a.compareTo(nil) == 1
b = FileNode('b', 200)
assert a.compareTo(b) < 0
assert b.compareTo(a) > 0
c = FileNode('c', 100)
assert a.compareTo(c) == 0
var _pathName as String
var _numBytes as decimal
cue init(pathName as String, numBytes as decimal)
_pathName = pathName
_numBytes = numBytes
get pathName from _pathName
get numBytes from _numBytes
def compareTo(obj as Object?) as int
require
obj is nil or obj inherits FileNode
body
if obj is nil
return 1
else if obj inherits FileNode
return _numBytes.compareTo(obj.numBytes)
else
throw FallThroughException(obj)
class Dir
"""
Dir does the work of scanning the files and subdirectories.
This results in a tree-like structure that matches the file system.
"""
shared
var _sep as char # Path.directorySeparatorChar
var _di as DirectoryInfo
var _dir as Dir? # parent directory
var _baseName as String
var _pathName as String
var _files as List<of FileInfo>
var _subdirs as List<of Dir>
var _count as int
var _numBytes as decimal # using 'decimal' because FileInfo.length returns int64
cue init(pathName as String)
""" Creates a Dir from a filesystem path string. Scans all subdirs. """
_di = DirectoryInfo(pathName)
_pathName = pathName
_baseName = pathName
_init(nil)
cue init(di as DirectoryInfo, parentDir as Dir)
""" Creates a Dir from a parent Dir. Scans all subdirs. """
_di = di
_baseName = di.name
_pathName = di.fullName
_init(parentDir)
get count from _count
get numBytes from _numBytes
def _init(parentDir as Dir?)
assert _baseName.length
_sep = Path.directorySeparatorChar
_dir = parentDir
if _dir, _pathName = '[_dir._pathName][_sep][_baseName]'
_files = List<of FileInfo>()
_subdirs = List<of Dir>()
.scan
def scan
assert _pathName.length
try
dirInfo = DirectoryInfo(_pathName)
catch IOException
print 'warning: Unreadable directory:', _pathName
return
try
files = dirInfo.getFiles
catch IOException
print 'warning: Unreadable files in directory:', _pathName
return
for fi in files
try
_files.add(fi)
_count += 1
_numBytes += fi.length to decimal
catch IOException
print 'warning: Cannot scan file:', fi.fullName
for di in dirInfo.getDirectories
d = Dir(di, this)
_subdirs.add(d)
_count += d.count
_numBytes += d.numBytes
get toList as List<of FileNode>
"""
Returns a flat list containing self, all files and all subdirs (recursively).
"""
t = List<of FileNode>()
.populateList(t)
return t
def populateList(t as List<of FileNode>) is protected
ensure
t.count > old t.count
body
t.add(FileNode(_pathName, _numBytes))
for fi in _files
try
t.add(FileNode(fi.fullName, fi.length))
catch IOException
print 'warning: Cannot scan file:', fi.fullName
for subdir in _subdirs
subdir.populateList(t)
class Sizes
test
assert _topPercent > 0.0 and _topPercent <= 1.0
assert _divisor > 0.0
assert _minSize >= 0.0
shared
var _topPercent = 0.1
var _divisor = 1024 * 1024.0 # megabyte
var _minSize = 1.0 # expressed in terms of _divisor
var _divisorLabel = 'MB'
def main
print 'Scanning...'
Console.out.flush
start = DateTime.now
args = CobraCore.commandLineArgs
dirName = if(args.count > 1, args[1], Directory.getCurrentDirectory)
topDir = Dir(dirName)
duration = DateTime.now.subtract(start)
print
print '[topDir.count] directories and files read'
print '[duration.totalSeconds:N] seconds to read'
print '[topDir.numBytes/_divisor:N] [_divisorLabel]'
print
files = topDir.toList
files.sort
files.reverse
n = (files.count*_topPercent) to int
for i in n
f = files[i]
size = f.numBytes / _divisor
if size < _minSize, break # small files are uninteresting
#print '%4i %7.1f %s' % (i+1, size, f.pathname)
istr = (i+1).toString.padLeft(4)
sizestr = size.toString('0.0').padLeft(7)
print '[istr] [sizestr] [f.pathName]'
|