Skip to content

Instantly share code, notes, and snippets.

@h0rs3r4dish
Created March 18, 2010 22:54
Show Gist options
  • Save h0rs3r4dish/337016 to your computer and use it in GitHub Desktop.
Save h0rs3r4dish/337016 to your computer and use it in GitHub Desktop.
A pure-Ruby 'tree' utility
#!/usr/bin/ruby
=begin
Changelog for 1.7
* Fixed a bug where bottom-level directories did not, in fact, have their '/'
* Allowed the specification of flags and directories in any order. Thus, the
results of `tree -depth 1 test` and `tree test -depth 1` are identical, and
either can be used.
Changelog for 1.6.2
* Added a '/' for the very first, root directory
Changelog for 1.6.1
* Removed the extra, trailing '/' on symlinks
Changelog for 1.6
* Fixed the bug where even the last folder in a list would have '|' under it
for sub-items
Changelog for 1.5
* Added the -sym flag to follow symlinks
* Cleaned up the display_directory method to only execute the list.each block,
instead of treating the last entry differently in a separate but similar
chunk of code
* Made sure every folder had a '/' after it to clear up confusion
Changelog for 1.4
* Added the -depth flag to limit the depth of the tree
* Improved the flag system to allow for non-truth values
Changelog for 1.3
* Added the flag/config system
* Implemented the -app and -help flags
=end
USAGE = "\
A 'tree' replacement utility for systems. It displays a textual filesystem tree
of either the current directory, or one supplied via command-line argument. It
is not as featured as the UNIX version (no 'ls' colors, etc) but it works too.
usage: tree [options] path
options:
-app Display the inside of .app folders (Mac applications)
-depth N Limits the tree to N directories (0 being top-level)
-help Shows this message
-sym Follow symlinks instead of displaying their targets
example:
user@host$ tree path
path/
|-- file.txt
|-- link -> location
`-- folder/
`-- subfile1
Version 1.7. All code is released under the MIT license. Anybody can use it,
copy it, give it away, modify it, and so on. (c) 2010 Chris 'hr4dish' Sz."
def display_directory(path,offset='',depth=0)
list = Dir[path+'/*'] # Glob
return if list.length == 0
list.each { |f|
delim = (f == list.last) ? '`' : '|'
link = File.symlink? f
checkf = (link) ? File.readlink(f) : f
checkdir = File.directory? checkf
print offset+delim+'-- '+f.split('/').last # Just the name, thanks
print '/' if checkdir
dir = if checkdir then
if $config['depth'] == -1 or depth < $config['depth'] then
if f =~ /\.app$/ then
not $config['app']
elsif link then
$config['sym']
else
true
end
else
false
end
end
if dir then
delim = (delim == '`') ? ' ' : '|'
print "\n"
display_directory(f,offset+delim+' ',depth+1)
else
# Symlinked directories get a / too
print(' -> '+File.readlink(f)) if link
print "\n"
#print(( (checkdir) ? '/' : '' )+' -> '+File.readlink(f)) if link
end
}
end
$config = {
'app' => true,
'help' => false,
'depth' => -1,
'sym' => false
}
# Parse options
ARGV.each_with_index { |opt, i|
next unless opt[0] == 45
opt = opt[1..opt.length] # drop the -
if [true, false].include? $config[opt] then
$config[opt] = !$config[opt] # toggle
else
arg = ARGV[i + 1]
case $config[opt].class.to_s
when "Fixnum"
$config[opt] = arg.to_i
when "String"
$config[opt] = arg
end
ARGV.delete_at i+1
end
ARGV.delete_at i
}
if $config['help'] then
puts USAGE
exit 0
end
ARGV << '.' unless ARGV.length > 0
ARGV.each { |dir|
puts dir+"/"
display_directory dir
print "\n" unless ARGV.last == dir
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment