4
\$\begingroup\$

I have written a script, introduced here, that improves the Linux terminal experience. It basically displays the content of the terminals current folder in a nicer way than the 'ls' command does. I use it all the time myself.

#!/usr/bin/perl
###########################################################################
# role:  this program shows all files and directories in current location.
# about: its a replacement to the 'ls' command.
# about: different from the ls command, this program is easily cutomize-able and improve-able.
# about: the code is available, readable and change-able on site.
# tofix: the 'length' function fails when there are icelandic letters. have to find another solution.
# tofix: could figure out beforehand what the largest filename is, and then position the file-size category accordingly.
# tofix: also, the header should be either singular or plural, not both.
# tofix: sorting week1 .. week12 does not work correctly.
# tofix: add: type(eg .pm) and line_quantity.
###########################################################################


#get all the pathfiles at current path.
chdir("$ENV{PWD}") or die "$!";
opendir(CDIR, ".") or die "$!";
@pathfiles_and_directories_here=grep {/.*/} readdir CDIR;
foreach(@pathfiles_and_directories_here)  {$_="$ENV{PWD}"."/$_";}
close CDIR;
@pathfiles_and_directories_here=grep { !/\/\.{1,2}/ }
@pathfiles_and_directories_here;#dont want '.' and '..'.

#establish pathfiles, paths and files.
@pathfiles_here =grep(! -d,@pathfiles_and_directories_here);
@files_here     =grep(! -d,@pathfiles_and_directories_here);
foreach(@files_here)        {s/$ENV{PWD}\///g;}
@directories_here= grep(  -d,@pathfiles_and_directories_here);     
foreach(@directories_here)  {s/$ENV{PWD}\///g;}

#sort the pathfiles, files and directories.. alphabetically.
@files_here=sort_alphabetically(@files_here);
$file_quantity=($#files_here+1);
@pathfiles_here=sort_alphabetically(@pathfiles_here);

#seperate directories into if they start with a number or a letter.
my @directories_starting_with_letter= grep /\A\D.*/i, @directories_here;
my @directories_starting_with_number= grep /\A\d.*/, @directories_here;
#sort them accordingly
@directories_starting_with_number=sort_numerically(@directories_starting_with_number);
@directories_starting_with_letter=sort_alphabetically(@directories_starting_with_letter);
#put them back into the directory list.
undef @directories_here;
@directories_here=(@directories_starting_with_number,@directories_starting_with_letter);

$directory_quantity=($#directories_here+1);

#get file_sizes_byte_quantity and file_sizes_human_readable.
map($file_sizes_byte_quantity{$_}=-s ,@pathfiles_here);
%file_sizes_human_readable=make_values_human_readable(%file_sizes_byte_quantity);
#get total file size.
$total_file_size_bytes=get_total_file_size(%file_sizes_byte_quantity);
$total_file_size=make_byte_quantity_human_readable($total_file_size_bytes);

#get type with the most lines.
if($#files_here>=$#directories_here)  {$column_with_most_lines=$#files_here;}
if($#files_here<$#directories_here)   {$column_with_most_lines=$#directories_here;}
$column_with_most_lines+=2;

system "clear";
#spaces become underscored.
print STDOUT "\033\[32\;4mDirectories             \| Files                         Size                                           \n\033[0m";
for(my $cline=0; $cline<=$column_with_most_lines; $cline++){
    my $cpathfile=$pathfiles_here[$cline];
    my $cdirectory=$directories_here[$cline];
    my $cfilename=$files_here[$cline];$cfilename =~ s/$ENV{PWD}\///g;
    my $csize=$file_sizes_human_readable{$cpathfile};

    #fs=fillup_string.
    my $csize_fs=             get_fillup_string($csize,               10);
    my $cdirectory_fs=        get_fillup_string($cdirectory,          24);
    my $directory_quantity_fs=get_fillup_string($directory_quantity,  24);
    my $cfilename_fs=         get_fillup_string($cfilename,           30);
    my $file_size_fs=         get_fillup_string($total_file_size,     10);
    my $file_quantity_fs=     get_fillup_string($file_quantity,       30);

    #write the folder.
    if($cline<=($#directories_here+0))  {print STDOUT "\033\[1\;33m$cdirectory$cdirectory_fs\033[0m";}
    if($cline==($#directories_here+1))  {print STDOUT "\033\[62\;32m------------------------\033[0m"; $cdirectory_fs='';}
    if($cline==($#directories_here+2))  {print STDOUT "\033\[62\;32m$directory_quantity"."$directory_quantity_fs\033[0m";}
    if($cline>=($#directories_here+3))  {print STDOUT "\033\[62\;32m                        \033[0m";}

    #if at least one of them is still in their listing, then print the seperator, otherwise just spacebar.
    if($cline<=$#directories_here || $cline<=$#files_here)  {print STDOUT "\033\[32\;3m\|\033[0m";}
    else  {print STDOUT "\ ";}

    #write the file.
    if($cline<=($#files_here+0))  {print STDOUT " $cfilename$cfilename_fs$csize$csize_fs\n";}
    if($cline==($#files_here+1))  {print STDOUT "\033\[62\;32m------------------------------------------------------------------------------\n\033[0m";}
    if($cline==($#files_here+2))  {print STDOUT "\033\[62\;32m    $file_quantity$file_quantity_fs$total_file_size$file_size_fs\n\033[0m";}
    if($cline>=($#files_here+3))  {print STDOUT "\033\[62\;32m                        \n\033[0m";}
    }
undef $column_with_most_lines;
print STDOUT "\n";












###############################################
#this function has already been copied to modules.
sub get_fillup_string{
my $string=shift;
my $wanted_string_length=shift;

#function 'length' fails when there are icelandic letters. have to find another solution.
my $string_length=length $string;
my $missing_string_length=($wanted_string_length-$string_length);
while($missing_string_length>0)  {$fillup_string.=' '; $missing_string_length--;}

my $temp=$fillup_string; undef $fillup_string;
return $temp;
}
###############################################
#this function has already been copied to modules.
#arg: returns the array sent in, sorted alhpabetically.
sub sort_alphabetically{
my @sorted=sort{lc($a) cmp lc($b)} @_;
return @sorted;
}
###############################################
#this function has already been copied to modules.
#arg: returns the array sent in, sorted alhpabetically.
sub sort_numerically{
#my @sorted=sort @_;
#print STDOUT "";
#sleep 1;
my @sorted= sort {$a <=> $b} @_;
return @sorted;
}
##############################################
#this functino will never be copied to modules.
sub make_values_human_readable{
my %file_sizes_byte_quantity=@_;

foreach my $ckey (keys %file_sizes_byte_quantity){
    my $cvalue=$file_sizes_byte_quantity{$ckey};
    $file_sizes_human_readable{$ckey}=make_byte_quantity_human_readable($cvalue);
    }
return %file_sizes_human_readable;
}
####################################################
#this function has not been copied to modules.
sub make_byte_quantity_human_readable{
my $cvalue=shift;

#the size must use sensible units.
if($cvalue<=100000 && $cvalue>0){
    $cvalue/=1000;
    $cvalue=sprintf("%.1f", $cvalue);
    $cvalue="$cvalue".'kb';
    }
if($cvalue>100000){
    $cvalue/=1000000;
    $cvalue=sprintf("%.1f", $cvalue);
    $cvalue="$cvalue".'mb';
    }
#gb next time.
return $cvalue;
}
###############################################
#this function will never be copied to modules.
sub get_total_file_size{
my %file_sizes_byte_quantity=@_;

$total_file_size=0;
foreach my $ckey (keys %file_sizes_byte_quantity){
    $total_file_size+=$file_sizes_byte_quantity{$ckey};
    }
my $temp=$total_file_size; undef $temp;
return $total_file_size;
}
\$\endgroup\$
4
\$\begingroup\$

use strict; and use warnings; are missing. Add them and declare all your variables.

opendir(CDIR, ".") or die "$!";

I believe that it’s advised to use variables in modern Perl:

opendir(my $cdir, '.') or die $!;

@pathfiles_and_directories_here=grep {/.*/} readdir CDIR;

grep {/.*/} is meaningless since it accepts everything.

@pathfiles_and_directories_here=grep { !/\/\.{1,2}/ }
@pathfiles_and_directories_here;#dont want '.' and '..'.

Why didn’t you grep for this pattern right at the beginning? I also dislike the line break here, it makes the lines look like unrelated statements =>

my @files_and_dirs = grep { !/^\.{1,2}/ } readdir $cdir;

I’ve also shortened the variable name. The previous name was too long, and prevented, rather than helped, readability. In general, the long variable names make it really hard to discern any structure when looking over your code. Furthmore, the suffix _here after each variable name isn’t conveying useful information.

Next,

foreach(@pathfiles_and_directories_here)  {$_="$ENV{PWD}"."/$_";}

The string concatenation is redundant. Furthermore, this look looks like a place for map.

@files_and_dirs = map { "$ENV{PWD}/$_" } @files_and_dirs;

But why are you doing this anyway? Further down the road, you remove the file path again from the files and paths.

#establish pathfiles, paths and files.

You should explain what the difference between pathfiles and regular files is. The comment, as it stands, doesn’t tell the reader anything interesting.

I’m not convinced that sort_alphabetically and sort_numerically help readability. Just replace the calls by sort with the appropriate comparer, that should be readable enough, and is just as short.

Furthermore, a note on formatting: you need to indent your code properly and remove dead code. This:

sub sort_numerically{
#my @sorted=sort @_;
#print STDOUT "";
#sleep 1;
my @sorted= sort {$a <=> $b} @_;
return @sorted;
}

cries “sloppy”. Writing clean code is a fundamental part of maintainability.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.