Sherlock has asked for the wisdom of the Perl Monks concerning the following question:

I've read in the contents of a directory and I would like to sort them nicely: Subdirectories on top (in alphabetical order) and then files (in alphabetical order). I'm using the following code, which takes a reference to the array containing the directory contents as a parameter:
sub SortDirectory() { my $arrayRef = $_[0]; my @fileArray; my $currIndex = 0; my $i; for ( $i = 0; $i < @$arrayRef; $i++ ) { if ( (grep /\./, $$arrayRef[$i]) != "" ) { $fileArray[$currIndex++] = splice(@$arrayRef,$i--,1); } } push (@$arrayRef, @fileArray); }
This seems to work except for the directories "." and "..", which I would like to appear at the very top of the directory listing (hence, be placed at the beginning of the array). Since I'm just checking for the presence of a "." to determine if the entry is a subdirectory or a file, the directories "." and ".." are considered files as well.

I was wondering if there was a way to have grep search for a wildcard (i.e. look for a "." with any other character immediately after it). That might help me solve the problem. Of course, if someone knows a better way to accomplish what I'm doing, I'm open to suggestions at this point.

Thanks,
- Sherlock

Replies are listed 'Best First'.
Re: Using grep with wildcards
by chipmunk (Parson) on Apr 21, 2001 at 00:26 UTC
    Before answering your question, I would like to point out some areas in your code that could be improved.
    sub SortDirectory() { my $arrayRef = $_[0];
    You have specified an empty prototype by adding () after the subroutine name, but your subroutine actually takes an argument. Don't use prototypes unless you really need them. (In other words, leave off those parens.)
    my @fileArray; my $currIndex = 0; my $i; for ( $i = 0; $i < @$arrayRef; $i++ ) { if ( (grep /\./, $$arrayRef[$i]) != "" )
    grep is intended to be used for pulling elements from a list. Using grep on a scalar is silly. Additionally, you're comparing strings with the numeric comparison operator != rather than the string comparison operator ne.

    This would be more Perlish: if ($arrayRef->[$i] =~ /\./)

    { $fileArray[$currIndex++] = splice(@$arrayRef,$i--,1); } } push (@$arrayRef, @fileArray); }

     

    Unfortunately, your code is really just guessing as to whether each item is a file or a directory (you're clearly on a Windows machine; this /\./ test wouldn't work at all under Unix or MacOS). Instead, you should use the file test operators -d and/or -f to determine whether each item is a directory or a file:
    sub SortDirectory { my($path, $arrayRef) = @_; my(@dirArray, @fileArray); foreach (@$arrayRef) { if (-d "$path/$_") { push @dirArray, $_; } else { push @fileArray, $_; } } }
    Because it is necessary for the filetest operator to know where the file is, I am passing in the directory path as well as the list of contents.
Re: Using grep with wildcards
by the_slycer (Chaplain) on Apr 21, 2001 at 00:17 UTC
    A couple things.
    First read perlre for the answer to your grep question.
    However, directories can contain periods in their name, so that's not the best way to test :-)
    To test if it's a dir, use if -d $somename
      Thanks the_slycer, that line works great.

      - Sherlock
Re: Using grep with wildcards
by buckaduck (Chaplain) on Apr 21, 2001 at 01:17 UTC
    sub SortDirectory() { my $arrayRef = $_[0]; @$arrayRef = ( sort(grep { -d } @$arrayRef), # sort dirs sort(grep { -f } @$arrayRef)); # sort files }

    buckaduck

Re: Using grep with wildcards
by Chady (Priest) on Apr 21, 2001 at 00:23 UTC

    I have a feeling you're using grep in the wrong way.

    this is a powerful tool when used correctly.
    AFAIK, this is how it is used:

    @newArray = grep { condition } @arrayToFilter;

    the condition is usually a RegEx, so you can use any combination of RegEx so you call "wildcards" ie: \.. : a "." with any other character immediately after it.

    I hope this helps.. and eventually is correct :)

    Update: fixed HTML missing tag.
    He who asks will be a fool for five minutes, but he who doesn't ask will remain a fool for life.