Link to home
Start Free TrialLog in
Avatar of waffe
waffeFlag for United States of America

asked on

Count how many files are in a directory

I am looking for a perl script that can count the number of jpg files in a given directory. Can this be done? What does the script look like?
Avatar of jmcg
jmcg
Flag of United States of America image

One way to do it:

chdir $dir
my @files = <*.jpg>;
print "There are ", scalar(@files), " jpg files in directory $dir\n";
Avatar of waffe

ASKER

Works great, but can you make it so @files only = the last photo in the directory. Right now it = all of the photos in the directory.
SOLUTION
Avatar of ozo
ozo
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of waffe

ASKER

Sorry but that will not work because all of my jpg files are numbered starting with 1. So if I have 30 jpg then $files should = 30.jpg.
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of waffe

ASKER

LOL - No double posting here...

Not working just yet. This is what I got.

My files are numbered like:
1.jpg, 2.jpg, 3.jpg, etc...

I have tried both scripts above and I get the same results as the first code you gave me. The array always equals all of the photos in my folder.

Just take $imagelist[-1] if you want only the last one
Avatar of waffe

ASKER

When I use  $imagelist[-1] I always get 9.jpg if the dir had more then 9 files in it. If the dir had less the 9 files in it I get a correct output.
It worked for me, so I think we need to see some more details of your situation to know why it's not working for you: code of your script, list of actual filenames, etc.
Avatar of josephfluckiger
josephfluckiger

If 9.jpg is coming first it probably means that you are performing an ASCII comparison rather than a numerical one.

This might be because you are leaving out the "spaceship operator", the "<=>" which causes a numerical comparison. If you leave this out, the sort function by default performs an ASCII comparison which would result in "9.jpg" coming before  "10.jpg" (when reverse sorted). But jmcg's code should not have this problem as it is written.

for example I have files 1.jpg,2.jpg,3.jpg ... 30.jpg in a directory.
------------jmcg's solution causes 30.jpg to correctly come first-------------
chdir $dir;
my @images = glob "*.jpg";
my @images_sorted = map { $_->[1] }
        sort { $b->[0] <=> $a->[0] }
        map { /(\d+)/; [$1, $_] } @images;
-------------------------------------------------------------------------------------

-------------this code causes 9.jpg to incorrectly come first (notice the "gt")-------------------
chdir $dir;
my @images = glob "*.jpg";
my @images_sorted = map { $_->[1] }
        sort { $b->[0] gt $a->[0] }
        map { /(\d+)/; [$1, $_] } @images;
-------------------------------------------------------------------------------------

perhaps you are using some variation of the second.

Avatar of waffe

ASKER

Here is my perl script and my dir looks just like josephfluckiger's.

#!/usr/bin/perl -w

use CGI qw(:standard);
use strict;

my $galleryNum     = param('gallery');
my $dir                 = "/home/www/domain.com/data_base/gallery/gallery$galleryNum";

chdir $dir;
my @images = glob "*.jpg";
my @images_sorted = map { $_->[1] }
        sort { $b->[0] <=> $a->[0] }
        map { /(\d+)/; [$1, $_] } @images;


 print <<ENDHTML;

<HTML>
<HEAD>
<TITLE>Photos</TITLE>
</HEAD>
<BODY>
<BODY BGColor="000000">
<OBJECT classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"
WIDTH="550" HEIGHT="450"  id="http://www.domain.com/data_base/gallery/gallery.swf">
<PARAM NAME=movie VALUE="http://www.domain.com/data_base/gallery/gallery.swf?jpgNum=$images[-1]&gallery=$galleryNum">
<PARAM NAME=quality VALUE=high>
<PARAM NAME=bgcolor VALUE=#000000>

<EMBED src="http://www.domain.com/data_base/gallery/gallery.swf?jpgNum=$images[-1]&gallery=$galleryNum" quality=high bgcolor=#000000 WIDTH="550" HEIGHT="450"
NAME="http://www.doamin.com/data_base/gallery/gallery.swf" ALIGN="" TYPE="application/x-shockwave-flash"
PLUGINSPAGE="http://www.macromedia.com/go/getflashplayer">
</EMBED>
</OBJECT>
There are ", scalar($images[-1]), " jpg files in directory $dir\n
</BODY>
</HTML>
ENDHTML
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
and more importantly you also use it in the two lines:
------------------------currently incorrect------------------
<PARAM NAME=movie VALUE="http://www.domain.com/data_base/gallery/gallery.swf?jpgNum=$images[-1]&gallery=$galleryNum">
<EMBED src="http://www.domain.com/data_base/gallery/gallery.swf?jpgNum=$images[-1]&gallery=$galleryNum" quality=high bgcolor=#000000 WIDTH="550" HEIGHT="450"

------------------------corrected-----------------------------
<PARAM NAME=movie VALUE="http://www.domain.com/data_base/gallery/gallery.swf?jpgNum=$images_sorted[-1]&gallery=$galleryNum">
<EMBED src="http://www.domain.com/data_base/gallery/gallery.swf?jpgNum=$images_sorted[-1]&gallery=$galleryNum" quality=high bgcolor=#000000 WIDTH="550" HEIGHT="450"
And, since the array was sorted in reverse numerical order, the newest file is

$images_sorted[0]
Avatar of waffe

ASKER

That was it! My bad - right in front of my face and I missed it. I got to look alittle closer at the code you people give me. It has something to do with the exspressions, they are intimidateing. Side question - what is the best book out there for getting to know perl exspressions?
I first learned Perl from the Camel book (_Programming Perl_, Larry Wall et al, O'Reilly & Associates), but that was the first edition. The 2nd and 3rd editions strike me as being less suitable for learning from, but it's still an essential reference book.

This particular stack of map -- sort -- map is often known as a Schwartzian Transform. It _is_ a little intimidating at first. I think I was reading Randall Schwartz's articles on Perl for a year before I finally started to be able to make up my own devices based on this particular coding technique.
Thanks for the points. They are my first official expert points!  (jmcg did most the hard work)

Here is an excerpt from Programming Perl, 3rd Edition on the sort function:

-------------------------
29.2.159 sort

sort USERSUB LIST
sort BLOCK LIST
sort LIST

This function sorts the LIST and returns the sorted list value. By default, it sorts in standard string comparison order (undefined values sort before defined null strings, which sort before everything else). When the use locale pragma is in effect, sort LIST sorts LIST according to the current collation locale.

USERSUB, if given, is the name of a subroutine that returns an integer less than, equal to, or greater than 0, depending on how the elements of the list are to be ordered. (The handy <=> and cmp operators can be used to perform three-way numeric and string comparisons.) If a USERSUB is given but that function is undefined, sort raises an exception.

In the interests of efficiency, the normal calling code for subroutines is bypassed, with the following effects: the subroutine may not be a recursive subroutine (nor may you exit the block or routine with a loop control operator), and the two elements to be compared are not passed into the subroutine via @_, but rather by temporarily setting the global variables $a and $b in the package in which the sort was compiled (see the examples that follow). The variables $a and $b are aliases to the real values, so don't modify them in the subroutine.

The comparison subroutine is required to behave. If it returns inconsistent results (sometimes saying $x[1] is less than $x[2] and sometimes saying the opposite, for example), the results are not well defined. (That's another reason you shouldn't modify $a and $b.)

USERSUB may be a scalar variable name (unsubscripted), in which case the value provides either a symbolic or a hard reference to the actual subroutine to use. (A symbolic name rather than a hard reference is allowed even when the use strict 'refs' pragma is in effect.) In place of a USERSUB, you can provide a BLOCK as an anonymous, inline sort subroutine.

To do an ordinary numeric sort, say this:

sub numerically { $a <=> $b }
@sortedbynumber = sort numerically 53,29,11,32,7;

To sort in descending order, you could simply apply reverse after the sort, or you could reverse the order of $a and $b in the sort routine:

@descending = reverse sort numerically 53,29,11,32,7;

sub reverse_numerically { $b <=> $a }
@descending = sort reverse_numerically 53,29,11,32,7;

To sort strings without regard to case, run $a and $b through lc before comparing:

@unsorted = qw/sparrow Ostrich LARK catbird blueJAY/;
@sorted = sort { lc($a) cmp lc($b) } @unsorted;

(Under Unicode, the use of lc for case canonicalization is vaguely preferred to the use of uc, since some languages differentiate titlecase from uppercase. But that doesn't matter for basic ASCII sorting, and if you're going to do Unicode sorting right, your canonicalization routines are going to be a lot fancier than lc.)

Sorting hashes by value is a common use of the sort function. For example, if a %sales_amount hash records department sales, doing a hash lookup in the sort routine allows the hash keys to be sorted according to their corresponding values:

# sort from highest to lowest department sales
sub bysales { $sales_amount{$b} <=> $sales_amount{$a} }

for $dept (sort bysales keys %sale_amount) {
    print "$dept => $sales_amount{$dept}\n";
}

You can perform additional levels of sorting by cascading multiple comparisons using the || or or operators. This works nicely because the comparison operators conveniently return 0 for equivalence, causing them to fall through to the next comparison. Here, the hash keys are sorted first by their associated sales amounts and then by the keys themselves (in case two or more departments have the same sales amount):

sub by_sales_then_dept {
    $sales_amount{$b} <=> $sales_amount{$a}
       ||
    $a cmp $b
}

for $dept (sort by_sales_then_dept keys %sale_amount) {
    print "$dept => $sales_amount{$dept}\n";
}
Avatar of waffe

ASKER

Thanks for the info! I feel a bit dizzy after that.) That opens about a hundred questions josephfluckiger. Here is one more if you don't mind.

Please explain where $a and $b come from in the sort comand. Are they apart of the sort object? I see it like this:
@images = glob "*.jpg" is $a
@images_sorted = map { $_->[1] is $b

Is the correct way of looking at it?
perldoc -f sort
Regarding $a and $b:

No. The 'sort', 'map', and 'grep' commands are three perl functions that can take, as their second argument, a code block. For 'map' and 'grep', the $_ variable is used to pass in the "current" value of the array. For 'sort', two variables must be available in the comparison routine; these are $a and $b.

The structure of that formidable Schwartzian Transform is something like this:

@result = map UNDO_CODEBLOCK1 sort COMPARE map CODEBLOCK1 @array;

The COMPARE codeblock uses the $a and $b variables. The sort function calls the COMPARE block with pairs of elements from it's input array. The results from the COMPARE codeblock inform the sort function whether $a should be placed _before_ $b or _after_.

The purpose of CODEBLOCK1 is to process the input array into a data structure containing an easily manipulated sort key associated with some sort of handle or reference to the original data (it could copy the entire original data item into this data structure). The purpose of UNDO_CODEBLOCK1 is to take the array and essentially undo what CODEBLOCK1 did. It regurgitates th original data, but now in the order that was selected by 'sort'.
Any sort routine is basically a serious of comparisons. For example if you were to manually take the list [2,3,1] and sort in accending order, you would say: (none of this is perl code, it's just an example)

2 < 3 ? yes, don't swap
   [current list: 2,3,1]
2 < 1 ? no, swap
   [current list: 1,3,2]
3 < 2 ? no, swap
   [current list: 1,2,3]


$a and $b are the variables that the sort command uses to make this comparison. I think you could say they are "part of the sort function". But the last part of your statement with the two equal signs, I think is incorrect.