[CGM] : It's an invariable rule that however many megabytes/gigabytes/terabytes you have, eventually they will fill up. When that happens the next step is usually a frantic rush to see what old data can be deleted or compressed to make space for new stuff. This script is designed to help with that process. It basically just runs the Unix command '''du''' ("Disk Usage") and presents the output as a graphical tree, sorted so that the biggest directories/files are at the top. Once you have identified the files you need to work on, you can select them in the tree and copy/paste the pathnames to a shell. Notes: * The script needs the `treectrl` and `tooltip` packages. * It needs the Gnu version of `du` since that can also return file/directory modification times. * I experimented with making the script self-contained by not calling `du` but having the script read the file sizes itself as is done in the [du] script on this wiki. However this turned out to be significantly slower, which matters when you are trying to scan a big disk in a hurry. Also the separate `du` program accounts correctly for odd cases like files with multiple hard links, or sparse files where not all the data blocks actually exist on disk. ---- ====== #!/usr/local/bin/tcl # diskusage - display disk space used in tree gui # C. Macleod 24/12/10 - 6/9/20 if {[catch {package require Tk}]} { puts stderr "This program needs an X-window display." exit } ########################### Set up screen ############################ package require treectrl package require tooltip treectrl .tc -yscrollcommand {.ys set} -xscrollcommand {.xs set} -width 600 -height 400 .tc column create -text {Size kB} .tc column create -text {Mod Date} .tc column create -text {File Name} .tc element create el1 text .tc style create s1 .tc style elements s1 el1 .tc style layout s1 el1 -ipadx 2 .tc element configure el1 -fill {red active green selected} .tc configure -treecolumn 0 -selectmode extended tooltip::tooltip .tc {Select files/directories with keyboard or mouse. Control-C copies selected pathname(s). Right-click to copy Item/Pathnames/Details.} scrollbar .ys -command {.tc yview} -orient vertical scrollbar .xs -command {.tc xview} -orient horizontal grid .tc .ys grid .tc -sticky nsew grid .ys -sticky ns grid .xs -sticky ew grid columnconfigure . 0 -weight 1 grid columnconfigure . 1 -weight 0 grid rowconfigure . 0 -weight 1 grid rowconfigure . 1 -weight 0 ##################### Get and display sizes under specified path ###################### proc read_sizes ppath { set ::parent root set ::old_depth [llength [file split $ppath]] set cmd "| du -a -k --time --threshold=$::min_size" lappend cmd $ppath set du [open $cmd] fileevent $du readable [list read_item $du] } proc read_item du { global parent old_depth if {[gets $du line] == -1} { catch {close $du} incr ::done return } if {! [regexp -- {(\d+)\s+([\d\-]+\s[\d:]+)\s+(.*)} $line - size mtime path]} continue set depth [llength [file split $path]] for {set x $old_depth} {$x <= $depth} {incr x} { set isdir [expr {$x < $depth}] set isopen [expr {[.tc depth $parent] < 1}] set item [.tc item create -parent $parent -button $isdir -open $isopen] .tc item style set $item 0 s1 1 s1 2 s1 if {$isdir} { set parent $item .tc item text $item 0 0 set dpath $path for {set y $x} {$y < $depth} {incr y} { set dpath [file dir $dpath] } .tc item text $item 2 $dpath } } if {$depth < $old_depth} { set item $parent set parent [.tc item parent $item] } .tc item text $item 1 $mtime .tc item text $item 2 $path .tc item text $item 0 $size set old_depth $depth if {[.tc item isopen $parent]} { .tc item sort $parent -integer -decreasing } maybe_update } #################### Expand directory ##################### .tc notify bind .tc { expand %I } proc expand item { .tc item sort $item -integer -decreasing } ############# Update display but not more than once per 5 sec ############## set next_update_time [clock seconds] proc maybe_update {} { set now [clock seconds] if {$now >= $::next_update_time} { set ::next_update_time [expr {$now + 2}] update } } #################### Copy on right click or ctrl-C ##################### set my_selection {} proc my_copy text { set ::my_selection $text selection own . } selection handle . onSelect proc onSelect {offset maxChars} { return [string range $::my_selection $offset [expr {$offset+$maxChars}]] } menu .rmenu -tearoff 0 bind .tc <3> "right_click %x %y %X %Y" bind .tc {.tc selection add first last} bind .tc copy_pathnames proc right_click {x y X Y} { .tc identify -array ident $x $y if {$ident(where) eq "item"} { set text [.tc item text $ident(item) $ident(column)] } else { set text {} } .rmenu delete 0 end .rmenu add command -label "Copy item '$text'" -command [list my_copy $text] .rmenu add command -label "Copy selected pathnames" -accelerator ^C -command copy_pathnames .rmenu add command -label "Copy selected size/date/pathnames" -command copy_details tk_popup .rmenu $X $Y } proc selected_pathnames {} { set text {} foreach item [.tc selection get 0 end] { lappend text [.tc item text $item 2] } return $text } proc copy_pathnames {} {my_copy [selected_pathnames]} proc copy_details {} { set text {} foreach item [.tc selection get 0 end] { append text "[join [.tc item text $item] \t]\n" } my_copy $text } ########################### Start things up ############################ set min_size 100K if {[llength $argv]} { set filelist $argv } else { set filelist . } wm title . "Disk Usage: [pwd] $filelist" . configure -cursor watch focus .tc update foreach f $filelist { read_sizes $f vwait done } . config -cursor {} ====== ---- <> File | Unix | System Administration