Utility to print largest directories.
Help
dius.py -hDisk Usage 1.5
usage: dius.py [-h] [-c COUNT] [-s] [-w <12,187>] [directory]
Prints 'COUNT' largest directories found under top 'directory'.
positional arguments:
directory set top directory to analyze [/home/petr]
options:
-h, --help show this help message and exit
-c COUNT, --count COUNT
set number of largest directories to print [20]
-s, --silent suppress progress messages [false]
-w <12,187>, --width <12,187>
set console width for progress indicator [187]Example
dius.py /etc/Disk Usage 1.5
Analyzing /etc/
Found 385 directories with 2,592 files in 1 second (385.0 directories/s, 2,592.0 files/s)
1/385 44.0 MiB /etc/alternatives
2/385 3.7 MiB /etc/libreoffice/registry
3/385 1.3 MiB /etc/brltty/Contraction
4/385 962.0 KiB /etc/dictionaries-common
5/385 645.7 KiB /etc/ssl/certs
6/385 640.6 KiB /etc/brltty/Text
7/385 403.3 KiB /etc/
8/385 140.0 KiB /etc/apparmor.d/abstractions
9/385 137.5 KiB /etc/fonts/conf.d
10/385 120.9 KiB /etc/X11/app-defaults
11/385 120.1 KiB /etc/apparmor.d
12/385 117.3 KiB /etc/udev/rules.d
13/385 111.3 KiB /etc/lvm
14/385 106.9 KiB /etc/grub.d
15/385 83.5 KiB /etc/cloud/templates
16/385 82.9 KiB /etc/sane.d
17/385 71.5 KiB /etc/init.d
18/385 69.5 KiB /etc/console-setup
19/385 60.6 KiB /etc/speech-dispatcher/modules
20/385 52.5 KiB /etc/rc2.d
Other 1.6 MiB
Total 54.5 MiBListing
#!/usr/bin/env python3
# Copyright (c) 2026 ByteDeploy LLP
def dius():
"""Disk Usage"""
import argparse, colorama, enum, os, string, sys, time
TITLE = "Disk Usage"
VERSION = "1.5"
VERBOSE = False
COUNT = 20
MIN_WIDTH = 9 + 0 + 3 # "Scanning " + <no directory> + "..."
MAX_WIDTH = os.get_terminal_size().columns if sys.stdout.isatty() else 80
WIDTH = MAX_WIDTH
WIDTH = min(max(WIDTH, MIN_WIDTH), MAX_WIDTH)
def now(on="on", at="at"):
return time.strftime(f"{on + ' ' if on != '' else ''}%Y-%m-%d {at + " " if at != "" else ""}%H:%M:%S")
def printable(str, max):
str = "".join([char if char in string.printable else "?" for char in str])
if len(str) > max: str = str[:max-3] + "..."
return str
class Style(enum.Enum):
plain = enum.auto()
grouped = enum.auto()
gazillion = enum.auto()
STYLE = Style.gazillion
def plain(num):
return f"{num}"
def grouped(num):
return f"{num:,}"
def gazillion(num, suffix="B"):
for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi', 'Ri']:
if num < 1024:
return f"{num:.{1 if num % 1 > 0 else 0}f} {unit}{suffix}"
num /= 1024
return f"{num:.{1 if num % 1 > 0 else 0}f} {'Qi'}{suffix}"
def format(num, mode=Style.plain):
match mode:
case Style.grouped:
return grouped(num)
case Style.gazillion:
return gazillion(num)
case Style.plain | _:
return plain(num)
def places(num, min=0, mode=Style.plain):
return max(min, len(format(num, mode)))
colorama.init()
print(f"{TITLE} {VERSION}")
if VERBOSE:
print('\a', end="")
print(f"Python {sys.version}")
print(f"Command '{sys.argv[0]}'")
print(f"Arguments {sys.argv[1:]}")
print(f"Executed {now()}")
start = time.time()
parser = argparse.ArgumentParser(description="Prints 'COUNT' largest directories found under top 'directory'.")
parser.add_argument("directory", nargs="?", help="set top directory to analyze [%(default)s]", default=os.getcwd())
parser.add_argument("-c", "--count", help="set number of largest directories to print [%(default)s]", type=lambda count: max(int(count), 0), default=COUNT)
parser.add_argument("-s", "--silent", help="suppress progress messages [false]", action = "store_true", default=False)
parser.add_argument("-w", "--width", help="set console width for progress indicator [%(default)s]", metavar=f"<{MIN_WIDTH},{MAX_WIDTH}>", type=int, choices=range(MIN_WIDTH,MAX_WIDTH+1), default=WIDTH)
arguments = parser.parse_args()
directory = arguments.directory
count = arguments.count
silent = arguments.silent
width = arguments.width
if not silent:
print(f"Analyzing {directory}")
BACKTRACK = ("\r" if width < MAX_WIDTH else "\r") if sys.stdout.isatty() else "\n" # 2nd ? cursor up "\033[A"
started = time.time()
usage = {}
numFiles = 0
for path, dirs, files in os.walk(directory):
if not silent:
print(f"Scanning {printable(path, width-9): <{width-9}}", end=BACKTRACK)
files = list(filter(os.path.isfile, map(lambda file: os.path.join(path, file), files)))
numFiles += len(files)
usage[path] = sum(map(os.path.getsize, files))
if not silent:
print(f" {'': <{width-9}}", end=BACKTRACK)
seconds = max(1, round(time.time() - started)) # at least 1 second
dirRate = round(len(usage) / seconds, 1)
fileRate = round(numFiles / seconds, 1)
print("Found {} director{} with {} file{} in {} second{} ({} director{}/s, {} file{}/s)".format(
grouped(len(usage)), "y" if len(usage) == 1 else "ies",
grouped(numFiles), "" if numFiles == 1 else "s",
grouped(seconds), "" if seconds == 1 else "s",
grouped(dirRate), "y" if dirRate == 1 else "ies",
grouped(fileRate), "" if fileRate == 1 else "s"))
usage = sorted(usage.items(), key=lambda pathSize:(- pathSize[1], pathSize[0]))
widthCount = places(len(usage), min=2, mode=Style.grouped)
widthIndex = places(count, min=5-1-widthCount, mode=Style.grouped)
other = sum(map(lambda pathSize: pathSize[1], usage[count:]))
total = sum(map(lambda pathSize: pathSize[1], usage))
widthSize = max(
max(map(lambda pathSize: places(pathSize[1], mode=STYLE), usage[:count])) if count else 0,
places(other, mode=STYLE),
places(total, mode=STYLE))
for i, (path, size) in enumerate(usage[:count]):
print("{:>{}}/{:>{}} {:>{}} {}".format(
grouped(i+1), widthIndex,
grouped(len(usage)), widthCount,
format(size, mode=STYLE), widthSize,
path))
if (0 < count < len(usage)):
print(f"{'Other':>{widthIndex+1+widthCount}} {format(other, mode=STYLE):>{widthSize}}")
print(f"{'Total':>{widthIndex+1+widthCount}} {format(total, mode=STYLE):>{widthSize}}")
if VERBOSE:
elapsed = time.time() - start
seconds = round(elapsed)
minutes, seconds = divmod(seconds, 60)
hours, minutes = divmod(minutes, 60)
days, hours = divmod(hours, 24)
weeks, days = divmod(days, 7)
print(f"Completed {now()}")
print(f"Elapsed {weeks:d}w {days:d}d {hours:d}h {minutes:d}m {seconds:d}s ({elapsed:,.3f}s)")
print('\a', end="")
if '__main__' == __name__:
dius()