#!/usr/bin/perl use strict; use warnings; # listdir_seq # This script lists the files in the specified directory # but shows numerical sequences as one item. # E.g. the files file1.txt, file2.txt, file3.txt would show as file[1-3].txt # Cameron Hayne (macdev@hayne.net) September 2004 # bug fix: September 2007 # If no command-line argument, use the current directory my $dirname = scalar(@ARGV) >= 1 ? $ARGV[0]: "."; # function declarations sub read_directory($); sub group_files(@); sub print_groups(%); MAIN: { my @filenames = read_directory($dirname); print_groups(group_files(@filenames)); } exit; #----- FUNCTIONS ----- sub read_directory($) { my ($dirname) = @_; my @filenames = (); opendir(DIR, $dirname) or die "can't opendir $dirname: $!"; while (defined(my $filename = readdir(DIR))) { next if $filename =~ /^\.\.?$/; # skip . and .. push(@filenames, $filename); } closedir(DIR); return @filenames; } sub group_files(@) { my (@filenames) = @_; my %groups = (); foreach my $name (@filenames) { my ($start, $end, $number); if ($name =~ /^(.*\D)(\d+)(\D*)$/) { $start = $1; $number = $2; $end = $3; } else # filenames without a number in them { $start = $name; $end = ""; } my $key = "$start$end"; my $group = $groups{$key}; unless ($group) { my @numbers = (); $group = { start => $start, end => $end, numbers => \@numbers, noNumber => 0, }; $groups{$key} = $group; } if (defined($number)) { my $numbers_ref = $group->{numbers}; push(@$numbers_ref, $number); } else { $group->{noNumber}++; } } return %groups; } sub get_number_ranges(@) { my (@numbers) = @_; my @ranges = (); my $low = undef; my $prev = undef; my $range = undef; foreach my $n (sort {$a <=> $b} @numbers) { if (!defined($prev) or $n != ($prev + 1)) { push(@ranges, $range) if defined($range); $low = $n; $range = "$n"; } else { $range = "[$low-$n]"; } $prev = $n; } push(@ranges, $range) if defined($range); return @ranges; } sub print_groups(%) { my (%groups) = @_; foreach my $key (sort keys %groups) { my $group = $groups{$key}; my $start = $group->{start}; my $end = $group->{end}; my $numbers_ref = $group->{numbers}; if ($group->{noNumber}) { print "$start$end\n"; } if (defined($numbers_ref)) { my @ranges = get_number_ranges(@$numbers_ref); foreach my $range (@ranges) { print "$start$range$end\n"; } } } }