Updated `tree' program

François Pinard pinard at iro.umontreal.ca
Sun Jul 9 10:38:21 EDT 2000


Hi, gang!

I updated my `tree' program (a `du' displayer) once again, and thought
that some might like that I share it with you.  I was not satisfied with
all directory sizes being given in a single column, making difficult to
visually sort them out.  So I tried two things.  The `-m' option is surely
fun, but maybe not that useful for deep hierarchies, so I did not make it
the default.  The old behaviour is available with the `-p' option.

-------------- next part --------------
#!/usr/bin/env python
# Produce directory hierarchy with sizes, well sorted and indented.
# Copyright ? 1996, 1999, 2000 Progiciels Bourbeau-Pinard inc.
# Fran?ois Pinard <pinard at iro.umontreal.ca>, 1996.

# Idea from Pierre Rioux <riouxp at bch.umontreal.ca>, 1996-07-12.

"""\
Produce directory hierarchy with sizes, well sorted and indented.

Usage: tree [OPTION]... PATH...

  -p          display sizes vertically within a single prefix column
  -m          if not -p, mirror the structure while displaying sizes
  -b          within a directory, show biggest hierarchies first
  -l LEVEL    disregard directories more than LEVEL levels deep
  -s SIZE     disregard hierarchies smaller than SIZE Kb

By default, sizes are printed before the name, and sort is lexicographical.
Use `.' to list the current directory.
"""

import getopt, os, string, sys

class run:
    biggest = 0
    mirror = 0
    prefix = 0
    level = None
    size = None

def main(*arguments):
    if not arguments:
        sys.stdout.write(__doc__)
        sys.exit(0)
    # Decode options.
    options, arguments = getopt.getopt(arguments, 'bl:mps:')
    for option, value in options:
        if option == '-b':
            run.biggest = 1
        elif option == '-l':
            run.level = int(value)
        elif option == '-m':
            run.mirror = 1
        elif option == '-p':
            run.prefix = 1
        elif option == '-s':
            run.size = int(value)
    # Get the data, properly filtered.
    pairs = []
    for line in os.popen('du %s' % string.join(arguments)).readlines():
        text, path = string.split(line, '\t')
        size = int(text)
        if run.size is None or size >= run.size:
            split = string.split(path[:-1], '/')
            if run.level is None or len(split) <= run.level:
                pairs.append([split, size])
    # Sort the information as wanted.
    if run.biggest:
        value = {}
        for split, size in pairs:
            value[tuple(split)] = -size
        items = []
        for split, size in pairs:
            item = []
            for counter in range(len(split)):
                item.append((value[tuple(split[:counter+1])], split[counter]))
            items.append(item)
        items.sort()
        pairs = []
        for item in items:
            split = map(lambda((size, fragment)): fragment, item)
            size = -item[-1][0]
            pairs.append([split, size])
    else:
        pairs.sort()
    # Erase unneeded fragments of vertical lines.
    clean(pairs)
    pairs.reverse()
    clean(pairs)
    pairs.reverse()
    # Erase leftmost white columns.
    skip = 0
    split, size = pairs[0]
    while not split[skip]:
        skip = skip + 1
    # Format size information.
    margin_size = 0
    for pair in pairs:
        split, size = pair
        if run.prefix:
            pair[1] = '%d ' % size
        elif run.mirror:
            margin = '%d _/' % size
            items = split[skip:-1]
            items.reverse()
            for item in items:
                if item:
                    margin = margin + '  |'
                else:
                    margin = margin + '   '
            pair[1] = margin
        else:
            split[-1] = '%d %s' % (size, split[-1])
            pair[1] = ''
        margin_size = max(margin_size, len(pair[1]))
    # Produce resulting display.
    write = sys.stdout.write
    for split, margin in pairs:
        write(' ' * (margin_size - len(margin)))
        write(margin)
        for item in split[skip:-1]:
            if item:
                write('|  ')
            else:
                write('   ')
        write('\\_ %s\n' % split[-1])

def clean(pairs):
    # The basename is always written.  Intermediate directories normally
    # print as `|', yet None is used to inhibit the vertical line.
    draw = []
    for split, size in pairs:
        while len(draw) > len(split) - 1:
            del draw[-1]
        while len(draw) < len(split) - 1:
            draw.append(0)
        draw.append(1)
        for counter in range(len(split) - 1):
            if not draw[counter]:
                split[counter] = None

if __name__ == '__main__':
    apply(main, tuple(sys.argv[1:]))
-------------- next part --------------

-- 
Fran?ois Pinard   http://www.iro.umontreal.ca/~pinard


More information about the Python-list mailing list