Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

tree command

by Skeeve (Parson)
on Oct 13, 2015 at 08:09 UTC ( [id://1144644]=CUFP: print w/replies, xml ) Need Help??

I'm, every now and then, missing a "tree" command at places where I have to work.

And every now and then I "reinvent the wheel".

To put an end to this, I decided to present my current version here.

I hope it's useful to others and maybe I'll get some constructive feedback?

Update: Added AppleFritter's suggestion of hiding hidden files

#!/bin/env perl use strict; use warnings; use Getopt::Long qw(:config no_ignore_case); use Pod::Usage; my ( $showall, # show hidden files $showfiles, # show also files $showlinks, # show also a symlink's target ); help() unless GetOptions( a => \$showall, l => \$showlinks, f => \$showfiles, 'h|help' => \&help, 'm|man' => \&man, ); sub help { pod2usage(-verbose=>1); } sub man { pod2usage(-verbose=>2); } my $indent = ' '; my $indir = '--'; foreach my $path (@ARGV ? @ARGV : '.') { print $path,"\n"; traverse($path, ''); } sub traverse { my ($path, $depth) = @_; # Open the directory opendir my $dh, $path or die "Couldn't read $path: $!\n"; # Get all directory content - leaving out files unless asked for my(@content) = grep { not /^\.\.?$/ and ( $showfiles or not -f "$path/$_" ) and ( $showall or /^[^.]/ ) } readdir $dh; closedir $dh; # How many eitems are in the directory? my $count= scalar @content; # Prepare the standard indent my $i= $depth . '|' . $indent; # Print all the elements foreach my $sub (@content) { my $p= "$path/$sub"; # Prepare the last indent $i= $depth . ' ' . $indent unless --$count; print $depth, ($count ? '|' : '\\'), $indir , $sub; # Is it a link? if ( -l $p ) { # Shall they be shown as such if ($showlinks) { print " -> ", readlink $p; } print "\n"; next; } print "\n"; # Done unless it's a directory next unless -d $p; traverse($p, $i); } return; } =head1 NAME tree - A script to show a "graphical" representation of a directory st +ructure =head1 SYNOPSIS tree [options] [path...] =head1 DESCRIPTION tree will show a "graphical" representation of a directory structure, +including all files (when B<-f> specified) and link targets (when B<-l> specifie +d). =head1 OPTIONS =over 4 =item B<-f> Show also files. =item B<-l> Shhow also link targets. =item B<-h> =item B<--help> show a short help page. =item B<-m> =item B<--man> Show the man-page. =back =head1 AUTHOR Skeeve of perlmonks.org (perlmonks DOT org DOT Skeeve at XoXy dot net) Including ideas of Apple Fritter, a fellow Perl monk =cut

s$$([},&%#}/&/]+}%&{})*;#$&&s&&$^X.($'^"%]=\&(|?*{%
+.+=%;.#_}\&"^"-+%*).}%:##%}={~=~:.")&e&&s""`$''`"e

Replies are listed 'Best First'.
Re: tree command
by AppleFritter (Vicar) on Oct 13, 2015 at 10:01 UTC

    Cool! Here's a minor patch to make it ignore hidden entries (those starting with a dot) by default, and add an -a flag for showing those after all, similar to the one that ls has:

    --- tree.pl 2015-10-13 11:59:07.665138800 +0200 +++ tree_afr.pl 2015-10-13 11:58:27.145867300 +0200 @@ -5,11 +5,13 @@ use Pod::Usage; my ( + $showall, # show all, including entries starting with . $showfiles, # show also files $showlinks, # show also a symlink's target ); help() unless GetOptions( + a => \$showall, l => \$showlinks, f => \$showfiles, 'h|help' => \&help, @@ -33,6 +35,7 @@ opendir my $dh, $path or die "Couldn't read $path: $!\n"; # Get all directory content - leaving out files unless asked for my(@content) = grep { !/^\.\.?$/ and ( $showfiles or not -f "$pat +h/$_" ) } readdir $dh; + @content = grep { !/^\./ } @content unless $showall; closedir $dh; # How many eitems are in the directory?

    Without this I get a huge tree full of subtrees like .cpan, .git and so on.

    EDIT: since this is such a useful little thing, I couldn't keep myself from changing it to Unicode box-drawing characters and also formatting the code according to my preferences:

      Thanks for the idea. (++) I've updated the original post with my version of your idea.


      s$$([},&%#}/&/]+}%&{})*;#$&&s&&$^X.($'^"%]=\&(|?*{%
      +.+=%;.#_}\&"^"-+%*).}%:##%}={~=~:.")&e&&s""`$''`"e
Re: tree command
by RichardK (Parson) on Oct 13, 2015 at 10:49 UTC

    You're iterating over the list of files twice, once in the grep & once in the foreach, and you don't need to do that. Also glob is easier to use than opendir/readdir, IMHO. So you could have done something like this

    use v5.20; use warnings; use autodie qw/:all/; use File::Glob qw/:bsd_glob/; use File::Spec; use File::Basename; sub showtree { my ($dir,$indent) = @_; $indent .= ' '; my @list = bsd_glob(File::Spec->catfile($dir,'*')); foreach my $f (@list) { if (-d $f) { say $indent,'+-',basename($f); showtree($f,$indent . '|'); } if (-l $f) { say $indent,'+-',basename($f),' --> ',readlink($f); } } } showtree($ARGV[0] // '.');

      I'm iterating twice because I need to find the number of entries first. That way I know when to finish the (sub-)tree.


      s$$([},&%#}/&/]+}%&{})*;#$&&s&&$^X.($'^"%]=\&(|?*{%
      +.+=%;.#_}\&"^"-+%*).}%:##%}={~=~:.")&e&&s""`$''`"e

        No, you can know the length of the list and still only process it once. The two things aren't related at all.

Re: tree command
by hippo (Bishop) on Oct 13, 2015 at 12:27 UTC
    And every now and then I "reinvent the wheel". To put an end to this, I decided to present my current version here.

    It's always good to share! Could you perhaps summarise how your version differs from this implementation on CPAN? Thanks.

      I could, if I knew that version.

      Anyhow: Thanks for pointing to it. (++) I never thought that a tree command could be found on CPAN. I thought it's just for modules.


      s$$([},&%#}/&/]+}%&{})*;#$&&s&&$^X.($'^"%]=\&(|?*{%
      +.+=%;.#_}\&"^"-+%*).}%:##%}={~=~:.")&e&&s""`$''`"e
Re: tree command
by choroba (Cardinal) on Oct 14, 2015 at 15:58 UTC
    Reminded me of the Parse 'Pretty' Tree, where I tried to parse the output tree and reconstruct the underlying structure.
    لսႽ† ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
Re: tree command
by fishy (Friar) on Oct 16, 2015 at 20:02 UTC

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: CUFP [id://1144644]
Approved by Corion
Front-paged by AppleFritter
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others romping around the Monastery: (12)
As of 2024-04-16 07:59 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found