#!/usr/bin/perl # # epoch # # Give the raw epoch times for the named files. Output is always # one line per row. # # Revision History: # 1.0 Initial version. 130202 # 1.1 Added license info. 180616 # # Written by Wayne Morrison, 130202. # # Copyright 2013 Wayne Morrison # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # use strict; # use Getopt::Long qw(:config no_ignore_case_always); # normally use this... use Getopt::Long qw(:config bundling_override); # # Version information. # my $NAME = "epoch"; my $VERS = "$NAME version: 1.1"; #------------------------------------------------------------------------ # # Stuff for command-line options. # my %opts = (); # Filled option array. my @opts = ( 'a', # List dot files. 'd', # Don't recurse into directories. 'H', # Follow symlinks on cmdline. 'P', # Don't follow symlinks. 'L', # Follow all symlinks. (unsupported) 'help|h|he|hel', # Give help message. 'Version|V', # Give version message. ); my $aflag = 0; # Flag for dot files. my $dflag = 0; # Flag for no-recursion. my $hflag = 0; # Flag for following cmdline symlinks. my $pflag = 0; # Flag for not following symlinks. my $lflag = 0; # Flag for following all symlinks. #--------------------------------------------------------- my $DEF_FOLLOW = 1; # Follow symlinks by default. my $recurse; # Recursion flag. my $follow; # Follow-symlinks flag. my @names = (); # List of filenames. my @times = (); # List of files' times. my $maxlen = -1; # Maximum length of names. #------------------------------------------------------------------------ main(); exit(0); #------------------------------------------------------------------------ # Routine: main() # # Purpose: Do everything. # sub main { # # Parse our command line. # opts(); # # Adjust the recursion flag. # $recurse = $dflag ? 0 : 1; # # Gather the time data on the specified files. # cronus($recurse,@ARGV); # # Display the data we've gathered. # prtdata(); } #------------------------------------------------------------------------ # Routine: opts() # sub opts { # # Get the options. # GetOptions(\%opts,@opts) || usage(); # # Look for arguments. # usage() if(defined($opts{'help'})); version() if(defined($opts{'Version'})); $aflag = 1 if(defined($opts{'a'})); $dflag = 1 if(defined($opts{'d'})); $hflag = 1 if(defined($opts{'H'})); $pflag = 1 if(defined($opts{'P'})); $lflag = 1 if(defined($opts{'L'})); # # If we were given no arguments at all, then we'll list the # contents of the current directory. # $ARGV[0] = '.' if(@ARGV == 0); # # By default, we won't follow symlinks. # $follow = $DEF_FOLLOW; # # The -H flag will make us follow symlinks. # $follow = 1 if($hflag); # # The -P flag will make us not follow symlinks. # $follow = 0 if($pflag); # # If -P is combined with -H, then we'll use the default behavior # for symlinks. # $follow = $DEF_FOLLOW if($hflag && $pflag); # # Appease Cthulhu. # if($hflag && $pflag && $lflag) { print "Ph'nglui mglw'nafh Cthulhu R'lyeh wgah'nagl fhtagn\n"; exit(28); } # # Check for unsupported behavior. # if($lflag) { print STDERR "-L is not currently supported.\n"; exit(1); } } #------------------------------------------------------------------------ # Routine: cronus() # # Purpose: Gather time information on the named files. We'll handle # directories, symbolic links, and other files. # sub cronus { my $recflag = shift; # Directory-recursion flag. my @flist = @_; # List of files whose data we'll get. # # Get the times for the named files. # foreach my $fn (@flist) { # # Handle unfollowed symbolic links. # if((-l $fn) && (! $follow)) { nodedata($fn,$follow); } # # Handle directories and followed symbolic links. # elsif(-d $fn) { $fn =~ s/\/+$//; if($recflag) { my $globstr; # String to glob(). # # Figure out exactly what we're looking for. # $globstr = "$fn/*"; $globstr .= " $fn/.*" if($aflag); # # Get stats for the directory's entries. # foreach my $subfn (sort(glob($globstr))) { cronus(0,$subfn); } } else { nodedata($fn,1); } } # # Handle everything else. # else { nodedata($fn,$follow); } } } #------------------------------------------------------------------------ # Routine: nodedata() # # Purpose: Gather statistics on the given node. If it's a symbolic # link, we'll get the infor for the link or the linked file # depending on how we were called. # # The node name and its last-modify time will be saved in # the @names and @times arrays. The length of the longest # name will be saved in $maxlen. # sub nodedata { my $fn = shift; # File whose data will be retrieved. my $followflag = shift; # Flag indicating symlink following. my @filedata; # Data for the given file. # # Get the file statistics, based on whether or not we're supposed # to follow symlinks. # if($followflag) { @filedata = stat($fn); } else { @filedata = lstat($fn); } # # Handle the stats. # if(@filedata == ()) { print STDERR "cannot stat \"$fn\"\n"; } else { my $len; # Length of this filename. $fn =~ s/^\.\/+//; # # Save the data for this node. # push @names, $fn; push @times, $filedata[9]; # # Save this name's length if it's the longest we've seen. # $len = length($fn); $maxlen = $len if($len > $maxlen); } } #------------------------------------------------------------------------ # Routine: prtdata() # sub prtdata { # # Print the data. # for(my $ind=0; $ind < @names; $ind++) { printf("%*s %s\n", -$maxlen, $names[$ind], $times[$ind]); } } #------------------------------------------------------------------------ # Routine: usage() # sub usage { print "usage: $0 [-a | -d | -H | -P | -h] \n"; exit(0); } #------------------------------------------------------------------------ # Routine: version() # sub version { print "$VERS\n"; exit(0); } ############################################################################### =pod =head1 NAME epoch - Displays the raw epoch time of the last modification for the named files =head1 SYNOPSIS epoch [options] =head1 DESCRIPTION The B command displays the raw epoch times of the last-modification time for the named files and directories. If no files are given on the command line, then the contents of the current directory will be listed. If a directory is given on the command line, then its contents will be listed. Normally, B will not list files whose names begin with a dot (.) when listing a directory. This may be overridden with the B<-a> option. B follows the behavior of B in following symbolic links. Options allow control of this behavior. Output is always one line per row. =head1 OPTIONS The following options are handled by B: =over 4 =item B<-a> List directory entries whose names begin with a dot, as well as other entries. =item B<-d> Directories are displayed as regular files; their contents are not displayed. =item B<-H> Symbolic links on the command line are followed. This is the default behavior. =item B<-P> If a command-line argument is a symbolic link, the link itself will be listed rather than the node the link references. This option cancels the B<-H> option, reverting symbolic-link behavior to the default. =item B<-help> Displays a usage message. =back =head1 LICENSE Copyright 2013 Wayne Morrison Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. =head1 AUTHOR Wayne Morrison, wayne@waynemorrison.com =head1 SEE ALSO B =cut