CGI/Perl Guide | Learning Center | Forums | Advertise | Login
Site Search: in

  Main Index MAIN
INDEX
Search Posts SEARCH
POSTS
Who's Online WHO'S
ONLINE
Log in LOG
IN

Home: Perl Programming Help: Intermediate:
creating directory list?

 



sirxris
stranger

Jun 21, 2001, 7:44 PM

Post #1 of 10 (1030 views)
creating directory list? Can't Post

I was attempting to create a simple script that reads a directory, finds all the files that include ???_icn.jpg, creates an array and then lists them with the image and a link. Any ideas? Sorry for another stupid newbie question, but i guess we all gotta start somewhere.. thx



mhx
Enthusiast / Moderator

Jun 21, 2001, 10:17 PM

Post #2 of 10 (1023 views)
Re: creating directory list? [In reply to] Can't Post

Hi sirxris,

I hope you can draw all information you need from this example:

Code
#!/bin/perl -w 
use strict;

# This is the directory you want to search for images
my $dir = '/users/mholland/src/perl/test/';

# Open the directory
opendir DIR, $dir or die "Cannot open directory `$dir': $!\n";

# - read all files (readdir DIR)
# - grep those that end with '_icn.jpg' (grep /regex/, array)
# - put the filenames into array @images
my @images = grep /_icn\.jpg$/, readdir DIR;

# Close the directory
closedir DIR;

# Print HTML header (I think you wanted HTML)...
# qq(text) does the same as "text", but you can use " inside
print qq(<HTML><HEAD></HEAD><BODY>);

# Print a sorted list of the images
# The foreach iterates over all array elements and stores the current
# array element into the magical variable `$_'. The sort routine sorts
# an array and returns the sorted array.
print qq(<IMG SRC="$dir$_"><A HREF="$dir$_">Link</A>) foreach sort @images;

# Print HTML footer
print qq(</BODY></HTML>);

If you strip the comments, this is just

Code
#!/bin/perl -w 
use strict;

my $dir = '/users/mholland/src/perl/test/';

opendir DIR, $dir or die "Cannot open directory `$dir': $!\n";
my @images = grep /_icn\.jpg$/, readdir DIR;
closedir DIR;

print qq(<HTML><HEAD></HEAD><BODY>);
print qq(<IMG SRC="$dir$_"><A HREF="$dir$_">Link</A>) foreach sort @images;
print qq(</BODY></HTML>);

The HTML output is rather simple minded, so I hope you know some HTML to make it better. I also hope you understand my script or can at least draw some useful information from it.

-- Marcus



Jasmine
Administrator

Jun 22, 2001, 10:30 AM

Post #3 of 10 (1016 views)
Re: creating directory list? [In reply to] Can't Post

Marcus,

I'm sorry, but the link based on the code you provided will be <A HREF="'/users/mholland/src/perl/test/image_icn.jpg" which won't work.

Here's the alternative I've been working on, but you beat me to the punch :)


Code
#!/usr/bin/perl -wT 

use CGI qw /:standard/;
use CGI::Carp qw /fatalsToBrowser/;
use strict;

my $required_beginning = '';
my $required_ending = '.gif';
my $html = 'http://devserver/~mapsamples/images';
my $path = 'G:/home/mapsamples/www/images';

my @linked_images =
map {
a( { -href => 'http://url-to-go-to.com' }, # url
img( { -src => "$html$_", -border => 0 } ) # linked image
)
}
map { /^$path?(.*)$/ }
grep { -B } glob "$path/$required_beginning*$required_ending";

print header, table( map { Tr( th( $_ ) ), "\n" } @linked_images );

Now, for the explanation...


Always use -wT for CGI scripts.

Code
#!/usr/bin/perl -wT


Use CGI.pm for some html stuff:

Code
use CGI qw /:standard/;

Inserting the next line will output most fatal errors to the browser instead of seeing the infamous "Internal Server Error"

Code
use CGI::Carp qw /fatalsToBrowser/;

Always

Code
use strict;


Though you're looking for a specific file ending, I've added a specific beginning option. If you don't need it, just keep it blank like it is.

Code
my $required_beginning = '';


Here's where you add the required file ending

Code
my $required_ending    = '.gif';


Enter the http url to the files

Code
my $html               = 'http://devserver/~mapsamples/images';


Enter the server path to the files

Code
my $path               = 'G:/home/mapsamples//www/images';



Now for the fun part... We're going to create a new array with linked images based on the files found in the last line of this code portion.

Code
my @linked_images =


This creates the link and image tag. edit this portion to your needs. This action is done last.

Code
    map { 
a( { -href => 'http://url-to-go-to.com' }, # url
img( { -src => "$html$_", -border => 0 } ) # linked image
)
}


This extracts everything after the path. This action is done second, and its results will be used by the map line above.

Code
    map { /^$path?(.*)$/ }

Get a list of matching files from the directory. grep is a search operator, the { -B } searches for binary files; glob allows you to use wildcards * in your search. This action is done first, and its results will be used by the map line above.


Code
    grep { -B } glob "$path/$required_beginning*$required_ending";


Last, print the html header and a table with one image per table row.

Code
print header, table( map { Tr( th( $_ ) ) } @linked_images );

Hope this helps!



mhx
Enthusiast / Moderator

Jun 22, 2001, 4:23 PM

Post #4 of 10 (1011 views)
Re: creating directory list? [In reply to] Can't Post

Hi Jasmine,


In Reply To
I'm sorry, but the link based on the code you provided will be <A HREF="'/users/mholland/src/perl/test/image_icn.jpg" which won't work.

Sure, it was on my local machine ;-) Won't work with HTTP of course.
I really like your solution because it looks quite similar to my usual CGI scripting. (I don't write many CGI scripts, but if I do my HTML is so bad that I like to use CGI::* always.)
My first intention was to write a script as simple as possible to make it easy to understand. Looking at your solution now, I must admit that (at least to me) it's even more readable.
What I like most about your script is the use of glob to get the file list. I've seen glob once or twice, but never felt the need to look up what I does. It's a really, really cool function, and I guess I'm going to use it a lot in the future. My first six months with Perl, I didn't know what map and grep were doing, and I can't imagine to live without the today :)
But the following thing kept me busy for a while:

In Reply To

Code
map { /^$path?(.*)$/ }


I was wondering what you were trying to achieve with the ?. The only thing that came to my mind was that you wanted the remove $path only if the string starts with $path. If this was not your intention, please tell me why the ? is in there and ignore the rest. Besides I guess every string should start with $path because of what glob returns, the ? doesn't make the whole $path optional, but only the last character of $path (which didn't make any sense to me). The reason is that variables in regular expressions are interpolated before the regex engine parses the regex. I've checked this use the regex debugger (with $path being d:/temp):

Code
Compiling REx `^d:/temp?(.*)$' 
size 16 first at 2
1: BOL(2)
2: EXACT <d:/tem>(5)
5: CURLY {0,1}(9)
7: EXACT <p>(0)
9: OPEN1(11)
11: STAR(13)
12: REG_ANY(0)
13: CLOSE1(15)
15: EOL(16)
16: END(0)

You can see an exact match on d:\tem followed by zero or one occurrences of p. I think you wanted the regex to be:

Code
map { /^(?:$path)?(.*)$/ }

As I said, forget about this if keeping the $path optional was not your intention, but I couldn't imagine anything else.
Finally, thanks again for introducing the glob function to me!!

-- Marcus



sirxris
stranger

Jun 22, 2001, 5:04 PM

Post #5 of 10 (1007 views)
Re: creating directory list? [In reply to] Can't Post

Well the thing works like a charm, except one little thing, how could i create another variable that removes the _icn.jpg so i could make the text next to the image not have the _icn.jpg ? Thanx a million, trillion you guys! If i could repay you, just tell me how



mhx
Enthusiast / Moderator

Jun 22, 2001, 5:42 PM

Post #6 of 10 (1006 views)
Re: creating directory list? [In reply to] Can't Post

Hi,

look at your post in the beginner forum. I've put the code there.

-- Marcus



Jasmine
Administrator

Jun 22, 2001, 6:12 PM

Post #7 of 10 (1004 views)
Re: creating directory list? [In reply to] Can't Post


In Reply To
What I like most about your script is the use of glob to get the file list.

Thanks :) The biggest thing to remember is that map modifies; grep searches.


In Reply To
I was wondering what you were trying to achieve with the ?. The only thing that came to my mind was that you wanted the remove $path only if the string starts with $path.

Actually, what that map was doing was stripping the path and "saving" everything after the path. I did this so the server path can be replaced with the http url to display the image in a browser. Also, when a scalar is tossed into a regex, ? isn't looking for just the last character of the scalar's value -- it's looking for the entire value of the scalar.


Code
my $path = 'g:/h/i/i'; 

my $file = "i'm_an_i";

my $testvar = "$path/$file"; # just the way grep/glob will return a file

$testvar =~ /^$path?(.*)$/;

print $1; #prints /i'm_an_i

So if it was just looking for the last character of $path, it would have stopped at the first i and made $1 /i/i'm_an_i but instead it correctly shows the desired result of /i'm_an_i

Hope this explains it -- if I've missed the mark of your question, please let me know.





mhx
Enthusiast / Moderator

Jun 22, 2001, 7:21 PM

Post #8 of 10 (1002 views)
Re: creating directory list? [In reply to] Can't Post

Hi,

I think you got me wrong. I wasn't saying you were looking only for the last character. What your regex does is look for the $path with the last character of $path being optional instead of the whole $path. Perhaps it's because I'm no native english speaker. Anyway, here's an example to emphasize my concern:

Code
#!/bin/perl -w 
use strict;

my $path = '/path/to/files';
my $other_path = '/another/path';

my $file = 'myfile';

my $test = "$path/$file";
my $other_test = "$other_path/$file";


# your original regex

print $test =~ /^$path?(.*)$/ # /myfile
? "$1\n" : "no match\n";
print $other_test =~ /^$path?(.*)$/ # no match
? "$1\n" : "no match\n";


# your regex without the ?

print $test =~ /^$path(.*)$/ # /myfile
? "$1\n" : "no match\n";
print $other_test =~ /^$path(.*)$/ # no match
? "$1\n" : "no match\n";


# my modified version of your regex

print $test =~ /^(?:$path)?(.*)$/ # /myfile
? "$1\n" : "no match\n";
print $other_test =~ /^(?:$path)?(.*)$/ # /another/path/myfile
? "$1\n" : "no match\n";

As far as I understand, you want to strip of the $path from the beginning of the string. But if the string doesn't begin with $path, you don't want to strip off anything.
If you look at the three regexes above, only my modified variant does this. For any other behaviour, I guess, the ? is completely useless, as you can see if you compare the first two regexes. It's useless because

In Reply To
Also, when a scalar is tossed into a regex, ? isn't looking for just the last character of the scalar's value -- it's looking for the entire value of the scalar.

is wrong. First, $path is interpolated, then the resulting string is passed to the regex engine. You can find this illustrated in Jeffrey Friedl's 'Mastering Regular Expression'. As a proof that your original regex has only the last character of $path optional, I've included the debug output of the regex engine for your original regex

Code
Compiling REx `^/path/to/files?(.*)$' 
size 18 first at 2
1: BOL(2)
2: EXACT </path/to/file>(7) <- exact match
7: CURLY {0,1}(11) <- {0,1} means optional
9: EXACT <s>(0) <- 's' is optional
11: OPEN1(13)
13: STAR(15)
14: REG_ANY(0)
15: CLOSE1(17)
17: EOL(18)
18: END(0)

and for my modified one

Code
Compiling REx `^(?:/path/to/files)?(.*)$' 
size 18 first at 2
1: BOL(2)
2: CURLYM[0] {0,1}(11)
4: EXACT </path/to/files>(9) <- this is optional
9: SUCCEED(0)
10: NOTHING(11)
11: OPEN1(13)
13: STAR(15)
14: REG_ANY(0)
15: CLOSE1(17)
17: EOL(18)
18: END(0)

Do you understand what I'm trying to express? Sorry if I cannot explain this understandable enough for you.

-- Marcus



(This post was edited by mhx on Jun 22, 2001, 6:22 PM)


Jasmine
Administrator

Jun 22, 2001, 11:27 PM

Post #9 of 10 (1000 views)
Re: creating directory list? [In reply to] Can't Post


In Reply To
First, $path is interpolated, then the resulting string is passed to the regex engine.

Well if it was in MRE, then I must be wrong :) Learn something new every day :) Thanks!


In Reply To
As far as I understand, you want to strip of the $path from the beginning of the string. But if the string doesn't begin with $path, you don't want to strip off anything.

The grep/glob line will not return anything that doesn't begin with $path, so worrying about a line that doesn't begin with $path isn't necessary. So actually, the ? was completely unnecessary because the line will always begin with $path anyway.


mhx
Enthusiast / Moderator

Jun 23, 2001, 12:11 AM

Post #10 of 10 (999 views)
Re: creating directory list? [In reply to] Can't Post

Hi,

In Reply To
Because I can't find it, I'll freely admit that I may be wrong and will appreciate it if you can point me to the page in MRE that corrects me (or somewhere else).

I would have put the page number in the last post if I could have been sure that it's on the same page in your english version as it is in my german version of the book. Anyway, it should at least be some pages before or after my pointer, so in my book ("Reguläre Ausdrücke", 1. ed, 1998, 1. corrected reprint, 2000) what I described is located in figure 7-1 on page 227. It's the first figure in the chapter "Reguläre Ausdrücke in Perl" (Regular Expressions in Perl) and is captioned "Parsing in Perl, vom Programm-Text bis zur Regex-Maschine" (Parsing in Perl, from source code to regex engine).

In Reply To
I can never find exactly what I'm looking for in MRE -- it's definitely not a reference book :)

I absolutely agree with you. It's good to read it from the first to the last page, but you can really forget about the index. I've been searching the book for quite some time, too.

In Reply To
Though my tired eyes can't find it, I seem to recall seeing something that supported what I said.

I wouldn't be surprised if there's an exception to the above in Perl regexes :-) If you should find what was on your mind, I'd also be glad to know about it. But for the case we're discussing, the way perl behaves and the debug output leave no doubt that interpolation is done before regex compilation.

In Reply To
The grep/glob line will not return anything that doesn't begin with $path, so worrying about a line that doesn't begin with $path isn't necessary. So actually, the ? was completely unnecessary because the line will always begin with $path anyway.

That was my first thought when I saw the ?. But because I didn't know about the glob function yet, I accepted the fact that it could return otherwise. The longer I think about it, the more illogical this appears to me.
But now, we can safely remove the ? and can both be happy. Wink
Anyway, thanks for a very interesting discussion! (It made me stay up until 4.30am, read lots about regex again, search some books and manpages, and - not to forget - learn about glob...)

-- Marcus

PS: This forum is really great! I've been to some german Perl forums, but they are definetely way behind this one.


 
 


Search for (options) Powered by Gossamer Forum v.1.2.0

Web Applications & Managed Hosting Powered by Gossamer Threads
Visit our Mailing List Archives