]> git.treefish.org Git - fex.git/blobdiff - bin/l
Original release 20160328
[fex.git] / bin / l
diff --git a/bin/l b/bin/l
index 93c4aca0307ac4c6d12055095834fa4816c87b0b..affd4a21f93475d1a0a24e24c4aaf7016bce703e 100755 (executable)
--- a/bin/l
+++ b/bin/l
@@ -1,10 +1,10 @@
 #!/usr/bin/perl -w
 #
 #!/usr/bin/perl -w
 #
-# l / ll / lf / llf -  substitute of the classic ls command
+# l / ll / lf / llf -  better replacement of the classic ls command
 #
 # Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
 #
 #
 # Author: Ulli Horlacher <framstag@rus.uni-stuttgart.de>
 #
-# Copyright: GNU General Public License 
+# Perl Artistic License
 
 use Cwd qw'abs_path';
 use File::Basename;
 
 use Cwd qw'abs_path';
 use File::Basename;
@@ -22,31 +22,24 @@ $ENV{LC_CTYPE} = 'C';
 # parse CLI arguments
 $opt_l = $opt_i = $opt_t = $opt_s = $opt_a = $opt_r = $opt_d = $opt_n = 0;
 $opt_L = $opt_N = $opt_c = $opt_u = $opt_S = $opt_R = $opt_z = $opt_h = 0;
 # parse CLI arguments
 $opt_l = $opt_i = $opt_t = $opt_s = $opt_a = $opt_r = $opt_d = $opt_n = 0;
 $opt_L = $opt_N = $opt_c = $opt_u = $opt_S = $opt_R = $opt_z = $opt_h = 0;
-$opt_U = 0;
-${'opt_*'} = ${'opt_?'} = 0;
+$opt_U = $opt_x = $opt_E = 0;
+${'opt_*'} = 0;
 $opt_m = $opt_f = $opt_F = $opt_D = '';
 $opt_m = $opt_f = $opt_F = $opt_D = '';
-&usage if !getopts('hdnlLNitcuarsUSRz*?m:f:D:F:') || $opt_h;
+getopts('hdnlLNitcuarsxUSREz*m:f:D:F:') or usage(1);
+usage(0) if $opt_h;
 $opt_z = 1 unless $opt_R;
 $opt_l = 1                            if $0 eq 'll';
 $opt_l = $opt_i = $opt_a = $opt_S = 1 if $0 eq 'lll';
 $opt_z = 1 unless $opt_R;
 $opt_l = 1                            if $0 eq 'll';
 $opt_l = $opt_i = $opt_a = $opt_S = 1 if $0 eq 'lll';
+&examples if $opt_E;
 if ($0 eq 'lf' or $0 eq 'llf') {
 if ($0 eq 'lf' or $0 eq 'llf') {
-  unless ($opt_F) {
-    $opt_F = shift;
-    unless (length $opt_F) {
-      print "find regexp: ";
-      chomp($opt_F = <STDIN>||'');
-    }
-  }
-  $opt_l = $0 if $0 eq 'llf';
-  $opt_F = '.' unless length $opt_F;
-  $opt_R = $opt_F;
+  $opt_F ||= shift or usage(1);
+  $opt_R ||= scalar(@ARGV) || ($opt_F eq '.');
+  $opt_l ||= $0 eq 'llf';
 }
 
 $postsort = $opt_t||$opt_s;
 $postproc = $postsort||$opt_z;
 
 }
 
 $postsort = $opt_t||$opt_s;
 $postproc = $postsort||$opt_z;
 
-&examples if ${'opt_?'};
-
 # mark for squeeze operation
 $z = $opt_z ? "\0" : '';
 
 # mark for squeeze operation
 $z = $opt_z ? "\0" : '';
 
@@ -83,7 +76,7 @@ if ($opt_D) {
     $newer = $1;
   }
 }
     $newer = $1;
   }
 }
-  
+
 # preselect date field number
 if    ($opt_c) { $sdf = 'c' }
 elsif ($opt_u) { $sdf = 'a' }
 # preselect date field number
 if    ($opt_c) { $sdf = 'c' }
 elsif ($opt_u) { $sdf = 'a' }
@@ -109,13 +102,13 @@ if (@LIST && $postproc) {
     $opt_z = '%'.$opt_z.'s ';
     @LIST = grep { s/\0 *([,\d\.\-]+) /sprintf($opt_z,$1)/e } @LIST;
   }
     $opt_z = '%'.$opt_z.'s ';
     @LIST = grep { s/\0 *([,\d\.\-]+) /sprintf($opt_z,$1)/e } @LIST;
   }
-  
+
   @LIST = reverse @LIST if $opt_r;
 
   if (not ($opt_t or $opt_U) and grep /^d[rR-][wW-][xX-]/,@LIST) {
     foreach (@LIST) { print if /^d/ }
     foreach (@LIST) { print unless /^d/ }
   @LIST = reverse @LIST if $opt_r;
 
   if (not ($opt_t or $opt_U) and grep /^d[rR-][wW-][xX-]/,@LIST) {
     foreach (@LIST) { print if /^d/ }
     foreach (@LIST) { print unless /^d/ }
-  } else { 
+  } else {
     print @LIST;
   }
 }
     print @LIST;
   }
 }
@@ -125,7 +118,7 @@ if ($opt_S && $SS) {
   print "$SS file(s):";
   printf " r=%d (%s Bytes)",$SS{'-'},&d3($Ss) if $SS{'-'};
   delete $SS{'-'};
   print "$SS file(s):";
   printf " r=%d (%s Bytes)",$SS{'-'},&d3($Ss) if $SS{'-'};
   delete $SS{'-'};
-  foreach my $type (qw(l d c b p s ?)) { 
+  foreach my $type (qw(l d c b p s ?)) {
     printf " %s=%d",$type,$SS{$type} if $SS{$type};
     delete $SS{$type};
   }
     printf " %s=%d",$type,$SS{$type} if $SS{$type};
     delete $SS{$type};
   }
@@ -137,7 +130,7 @@ exit ($found ? 0 : 1);
 
 
 # collect files and build file lists
 
 
 # collect files and build file lists
-# 
+#
 # INPUT: filenames
 #
 # GLOBAL: @LIST
 # INPUT: filenames
 #
 # GLOBAL: @LIST
@@ -147,10 +140,10 @@ sub collect {
 
   # loop over all argument files/directories
   foreach $f (@files) {
 
   # loop over all argument files/directories
   foreach $f (@files) {
-  
+
     # skip jed and emacs backup files
     # next if $f =~ /~$/ and not $opt_a and not $opt_l;
     # skip jed and emacs backup files
     # next if $f =~ /~$/ and not $opt_a and not $opt_l;
-    
+
     # recursive?
     if ($opt_R) {
 
     # recursive?
     if ($opt_R) {
 
@@ -165,16 +158,22 @@ sub collect {
       list($f);
 
       # traverse real subdirs
       list($f);
 
       # traverse real subdirs
-      if (-d $f and not -l $f) { 
+      if (-d $f and not -l $f) {
         $f =~ s:/*$:/:;
         $f =~ s:/*$:/:;
+        # skip other file systems on -x
+        if ($opt_x) {
+          my @pd = stat(dirname($f));
+          my @sd = stat($f);
+          next if $pd[0] ne $sd[0];
+        }
         collect(getfiles($f));
       }
         collect(getfiles($f));
       }
-      
+
     } else {
     } else {
-  
+
       # suppress trailing / on -d option
       $f =~ s:/$:: if $opt_d;
       # suppress trailing / on -d option
       $f =~ s:/$:: if $opt_d;
-    
+
       # on trailing / list subdirs, too
       if ($f =~ m:/$:) { &list(&getfiles($f)) }
       elsif ($f eq '') { &list('/') }
       # on trailing / list subdirs, too
       if ($f =~ m:/$:) { &list(&getfiles($f)) }
       elsif ($f eq '') { &list('/') }
@@ -188,7 +187,7 @@ sub collect {
         }
         list($f);
       }
         }
         list($f);
       }
-      
+
     }
   }
 }
     }
   }
 }
@@ -226,7 +225,7 @@ sub list {
 
     $day = $date;
     $day =~ s/\s.*//;
 
     $day = $date;
     $day =~ s/\s.*//;
-    
+
     if ($older) {
       next if $older =~ /-/ and $day gt $older;
       next if $older !~ /-/ and $dates{m} > time-$older;
     if ($older) {
       next if $older =~ /-/ and $day gt $older;
       next if $older !~ /-/ and $dates{m} > time-$older;
@@ -235,7 +234,7 @@ sub list {
       next if $newer =~ /-/ and $day lt $newer;
       next if $newer !~ /-/ and $dates{m} < time-$newer;
     }
       next if $newer =~ /-/ and $day lt $newer;
       next if $newer !~ /-/ and $dates{m} < time-$newer;
     }
-    
+
     if (defined $linkname) {
 
       # prepend sorting string
     if (defined $linkname) {
 
       # prepend sorting string
@@ -260,7 +259,7 @@ sub list {
            elsif ($i eq 'l') { $line .= sprintf '%3s ',   $links }
            elsif ($i eq 'i') { $line .= sprintf '%14s ',  $inode }
            elsif ($i eq 'd') { $line .= sprintf '%10s ',  $date }
            elsif ($i eq 'l') { $line .= sprintf '%3s ',   $links }
            elsif ($i eq 'i') { $line .= sprintf '%14s ',  $inode }
            elsif ($i eq 'd') { $line .= sprintf '%10s ',  $date }
-           elsif ($i eq 'a') { $line .= sprintf '%10s %10s %10s ', 
+           elsif ($i eq 'a') { $line .= sprintf '%10s %10s %10s ',
                                         $dates{'a'},$dates{'m'},$dates{'c'} }
          } else {
            if    ($i eq 'm') { $line .= $mode.' ' }
                                         $dates{'a'},$dates{'m'},$dates{'c'} }
          } else {
            if    ($i eq 'm') { $line .= $mode.' ' }
@@ -279,10 +278,10 @@ sub list {
                                         &isodate($dates{'c'}).' ' }
          }
        }
                                         &isodate($dates{'c'}).' ' }
          }
        }
-       
+
       # predefined formats
       } else {
       # predefined formats
       } else {
-      
+
        if ($opt_n) {
           if ($opt_l) { $line .= sprintf "%06o %6d %6d $z%15s %10d ",
                                         $mode,$uid,$gid,$size,$date }
        if ($opt_n) {
           if ($opt_l) { $line .= sprintf "%06o %6d %6d $z%15s %10d ",
                                         $mode,$uid,$gid,$size,$date }
@@ -294,25 +293,25 @@ sub list {
           else        { $line .= sprintf "%s $z%19s %s ",
                                         $mode,$size,substr($date,0,-3) }
         }
           else        { $line .= sprintf "%s $z%19s %s ",
                                         $mode,$size,substr($date,0,-3) }
         }
-       
+
        if ($opt_i)   { $line .= sprintf '%3s %10s ',$links,$inode }
       }
 
       $line .= $linkname."\n";
        if ($opt_i)   { $line .= sprintf '%3s %10s ',$links,$inode }
       }
 
       $line .= $linkname."\n";
-      
-      if ($postproc) { 
+
+      if ($postproc) {
         push @LIST,$line;
         push @LIST,$line;
-      } else { 
+      } else {
         $line =~ s/\0//;
         print $line;
       }
       $found++;
         $line =~ s/\0//;
         print $line;
       }
       $found++;
-      
+
     } else {
       lstat $file;
       warn "$0: cannot get dir-info for ".quote($file)." - $!\n";
     }
     } else {
       lstat $file;
       warn "$0: cannot get dir-info for ".quote($file)." - $!\n";
     }
-    
+
   }
 }
 
   }
 }
 
@@ -320,7 +319,7 @@ sub list {
 #
 # INPUT: file name
 #
 #
 # INPUT: file name
 #
-# OUTPUT: filename with linkname, inode, hard link count, size, mode string, 
+# OUTPUT: filename with linkname, inode, hard link count, size, mode string,
 #         UID, GID, isodate
 sub info {
   my $file = shift;
 #         UID, GID, isodate
 sub info {
   my $file = shift;
@@ -332,14 +331,14 @@ sub info {
 
   if ($opt_L) { @stat = stat $file }
   else        { @stat = lstat $file }
 
   if ($opt_L) { @stat = stat $file }
   else        { @stat = lstat $file }
-  
+
   if (@stat) {
   if (@stat) {
-  
+
     $inode = $stat[1];
     $bmode = $stat[2];
     $links = $stat[3];
     $inode = $stat[1];
     $bmode = $stat[2];
     $links = $stat[3];
-    %dates = ('m' => $stat[9], 
-              'a' => $stat[8], 
+    %dates = ('m' => $stat[9],
+              'a' => $stat[8],
              'c' => $stat[10]);
 
     if ($opt_n) {
              'c' => $stat[10]);
 
     if ($opt_n) {
@@ -351,7 +350,7 @@ sub info {
       $gid  = getgrgid($stat[5]) || $stat[5];
       $date = &isodate($dates{$sdf});
     }
       $gid  = getgrgid($stat[5]) || $stat[5];
       $date = &isodate($dates{$sdf});
     }
-    
+
     if    (-f _)           { $type = '-'; $size = $stat[7]; }
     elsif (!$opt_L && -l _) { $type = 'l'; }
     elsif (-d _)            { $type = 'd'; }
     if    (-f _)           { $type = '-'; $size = $stat[7]; }
     elsif (!$opt_L && -l _) { $type = 'l'; }
     elsif (-d _)            { $type = 'd'; }
@@ -377,7 +376,7 @@ sub info {
         $mode = $type.$mode;
       } else {
         # with short list display only effektive file access modes
         $mode = $type.$mode;
       } else {
         # with short list display only effektive file access modes
-        $mode = $type 
+        $mode = $type
                . (-r _ ? 'R' : '-')
                 . (-w _ ? 'W' : '-')
                 . (-x _ ? 'X' : '-');
                . (-r _ ? 'R' : '-')
                 . (-w _ ? 'W' : '-')
                 . (-x _ ? 'X' : '-');
@@ -407,7 +406,7 @@ sub info {
   }
 
   $size = &d3($size);
   }
 
   $size = &d3($size);
-  
+
   # determine longest size field
   if ($opt_z) {
     my $x = length $size;
   # determine longest size field
   if ($opt_z) {
     my $x = length $size;
@@ -422,7 +421,7 @@ sub info {
   }
   $mode =~ s/\+$//;
   #$mode .= ' ' unless $mode =~ /\+$/;
   }
   $mode =~ s/\+$//;
   #$mode .= ' ' unless $mode =~ /\+$/;
-  
+
   return ($linkname,$inode,$links,$size,$mode,$uid,$gid,$date,%dates);
 }
 
   return ($linkname,$inode,$links,$size,$mode,$uid,$gid,$date,%dates);
 }
 
@@ -454,22 +453,22 @@ sub getfiles {
 
   if (opendir D,$dir) {
     $dir = '' if $dir eq '.';
 
   if (opendir D,$dir) {
     $dir = '' if $dir eq '.';
-    while (defined($f = readdir D)) { 
-    
+    while (defined($f = readdir D)) {
+
       # skip . and .. pseudo-subdirs
       next if $f =~ m:(^|/)\.\.?/*$:;
       # skip ONTAP snapshot dir
       next if $f =~ m:(^|/)\.snapshot/*$:;
       # skip . and .. pseudo-subdirs
       next if $f =~ m:(^|/)\.\.?/*$:;
       # skip ONTAP snapshot dir
       next if $f =~ m:(^|/)\.snapshot/*$:;
-      
+
 
       # skip jed and emacs backup files
       # next if $f =~ /~$/ and not $opt_a and not $opt_l;
 
       # skip jed and emacs backup files
       # next if $f =~ /~$/ and not $opt_a and not $opt_l;
-      
+
       if ($f =~ /$opt_m/) {
         my $x = $dir.$f;
       if ($f =~ /$opt_m/) {
         my $x = $dir.$f;
-        if (not -l $x and -d $x and not ($opt_R or $postsort or $opt_U)) { 
+        if (not -l $x and -d $x and not ($opt_R or $postsort or $opt_U)) {
           push @dirs,$x;
           push @dirs,$x;
-        } else { 
+        } else {
           push @files,$x;
         }
       }
           push @files,$x;
         }
       }
@@ -482,7 +481,7 @@ sub getfiles {
   } else {
     warn "$0: cannot read $dir : $!\n";
   }
   } else {
     warn "$0: cannot read $dir : $!\n";
   }
-  
+
   return (@dirs,@files);
 }
 
   return (@dirs,@files);
 }
 
@@ -518,7 +517,7 @@ sub isodate {
 sub quote {
   local $_ = shift;
   my $mc = '\'\[\]\\\\ `"$?&<>$*()|{};';
 sub quote {
   local $_ = shift;
   my $mc = '\'\[\]\\\\ `"$?&<>$*()|{};';
-  
+
   unless (defined $_) {
     die "@_";
     @x = caller;
   unless (defined $_) {
     die "@_";
     @x = caller;
@@ -545,14 +544,17 @@ sub fmatch {
 
 
 sub usage {
 
 
 sub usage {
-  my $opts = '[-lastcuidnrzLRNS*] [-f format] [-D X:Y]';
-  if ($0 ne 'lf') { 
-    print "usage: $0 $opts [-F regexp] [file...]\n";
+  my $status = shift;
+  my $opts = '[-lastcuidnrzLRxNS*] [-f format] [-D X:Y]';
+  local *OUT = $status ? *STDERR : *STDOUT;
+
+  if ($0 ne 'lf') {
+    print OUT "usage: $0 $opts [-F regexp] [file...]\n";
   }
   $opts =~ s/R//;
   }
   $opts =~ s/R//;
-  print "usage: lf $opts regexp [directory...]\n";
-  print <<EOD;
-options: -l  long list
+  print OUT "usage: lf $opts regexp [directory...]\n";
+  print OUT <<EOD;
+options: -l  long list (implicit if called 'll')
          -a  list also .* files
          -s  sort by size
          -t  sort by time
          -a  list also .* files
          -s  sort by size
          -t  sort by time
@@ -566,32 +568,33 @@ options: -l  long list
          -z  squeeze size field (slows down output)
          -L  derefernce symbolic links
          -R  recursive into subdirs
          -z  squeeze size field (slows down output)
          -L  derefernce symbolic links
          -R  recursive into subdirs
+         -x  do not cross filesystem boundaries with -R
          -F  find files matching case insensitive regexp
          -N  show only normal (regular) files
          -S  print statistics summary at end
          -F  find files matching case insensitive regexp
          -N  show only normal (regular) files
          -S  print statistics summary at end
-         -*  list plain file names (without masking \\)
+         -*  list plain file names (without \\ masking)
         -f  user defined format output, format characters are:
             m=mode, u=user, g=group, s=size, l=hard links count, i=inode
             n=name only, d=date, a=access+modification+inodechange dates
         -f  user defined format output, format characters are:
             m=mode, u=user, g=group, s=size, l=hard links count, i=inode
             n=name only, d=date, a=access+modification+inodechange dates
-         -D  list only files newer than X and older than Y 
+         -D  list only files newer than X and older than Y
              XY format: NUMBER[smhd] (s=seconds, m=minutes, h=hours, d=days)
              XY format: YYYY-MM-DD (Y=year, M=month, D=day)
              XY format: NUMBER[smhd] (s=seconds, m=minutes, h=hours, d=days)
              XY format: YYYY-MM-DD (Y=year, M=month, D=day)
-         -?  show examples
+         -E  show examples
 EOD
 EOD
-  exit 2;
+  exit $status;
 }
 
 sub examples {
   print <<EOD;
 l *.c            # list files ending with .c
 l -la            # list all files in long format
 }
 
 sub examples {
   print <<EOD;
 l *.c            # list files ending with .c
 l -la            # list all files in long format
-l -Rrs           # list files recursive reverse sorted by size 
+l -Rrs           # list files recursive reverse sorted by size
 l -*f mus        # list files native names with format: mode+user+size
 l -D 10d:        # list files newer than 10 days
 ll               # list files long format (equal to: l -l)
 lll              # list files extra long format (equal to: l -liS)
 l -*f mus        # list files native names with format: mode+user+size
 l -D 10d:        # list files newer than 10 days
 ll               # list files long format (equal to: l -l)
 lll              # list files extra long format (equal to: l -liS)
-lf 'status.*mp3' # list files recursive matching regexp (equal to: l -RF)
-lf sda3 /dev     # list devices matching sda3 (equal to: l -RF sd3 /dev)                                                       
+lf 'status.*mp3' # list files matching regexp (equal to: l -F 'status.*mp3')
+lf sda1 /dev     # list devices matching sda1 (equal to: l -RF sda1 /dev)
 EOD
   exit;
 }
 EOD
   exit;
 }