views:

8628

answers:

8

I would like to execute ls in a Perl program as part of CGI script.

For this I used exec(ls), but this does not return from the exec call.

Is there a better way to get a listing of a directory in Perl?

+6  A: 

In order to get the output of a system command you need to use backticks.

$listing = `ls`;

However, Perl is good in dealing with directories for itself. I'd recommend using File::Find::Rule.

holli
+19  A: 

Exec doesn't return at all. If you wanted that, use system.

If you just want to read a directory, open/read/close-dir may be more appropriate.

opendir my($dh), $dirname or die "Couldn't open dir '$dirname': $!";
my @files = readdir $dh;
closedir $dh;
#print files...
Leon Timmermans
Check the return value of system commands: opendir... or die "... $!"Or "use autodie;" to make them fatal by default.I know it's just a nitpick, but when you're giving advice to beginners it's an important lesson for them.
tsee
Yeah, you're right, I'll add it. Thanks.
Leon Timmermans
In case someone is wondering why readdir and closedir aren't checked: both only have one possible error, 'invalid directory handle'. Since I opened it myself and checked it, I can safely assume it is valid. Also, in list context readddir can't be checked anyway.
Leon Timmermans
If you do NOT want dot files you can do: my @files = grep { !/^\./ } readdir $dh;
Ranguard
If you want files beginning with dot, but not `.` and `..` (for current and parent directory), then you want: `my @files = grep { !/^\.\.?$/ } readdir $dh;`.
Platinum Azure
+8  A: 

exec does not give control back to the perl program. system will, but it does not return the results of an ls, it returns a status code. tick marks `` will give you the output of our command, but is considered by some as unsafe.

Use the built in dir functions. opendir, readdir, and so on.

http://perldoc.perl.org/functions/opendir.html

http://perldoc.perl.org/functions/readdir.html

J.J.
A: 

I would recommend you have a look at IPC::Open3. It allows for far more control over the spawned process than system or the backticks do.

dsm
+3  A: 

Everyone else seems stuck on the exec portion of the question.

If you want a directory listing, use Perl's built-in glob or opendir. You don't need a separate process.

brian d foy
Specifically, glob does exactly what you want: returns an array of the names of files in a folder.
Robert P
A: 

On Linux, I prefer find:

my @files = map { chomp; $_ } `find`;
JDrago
A: 

Yet another example:

chdir $dir or die "Cannot chroot to $dir: $!\n";
my @files = glob("*.txt");
dynax60
+1  A: 

EDIT: Whoops! I thought you just wanted a listing of the directories... remove the 'directory' call to make this script do what you want it to...

Playing with filehandles is the wrong way to go in my opinion. The following is an example of using File::Find::Rule to find all the directories in a specified directory. It may seem like over kill for what you're doing, but later down the line it may be worth it.

First, my one line solution:

File::Find::Rule->maxdepth(1)->directory->in($base_dir);

Now a more drawn out version with comments. If you have File::Find::Rule installed you should be able to run this no problem. Don't fear the CPAN.

#!/usr/bin/perl

use strict;
use warnings;

# See http://search.cpan.org/~rclamp/File-Find-Rule-0.32/README
use File::Find::Rule;

# If a base directory was not past to the script, assume current working director
my $base_dir = shift // '.';
my $find_rule = File::Find::Rule->new;

# Do not descend past the first level
$find_rule->maxdepth(1);

# Only return directories
$find_rule->directory;

# Apply the rule and retrieve the subdirectories
my @sub_dirs = $find_rule->in($base_dir);

# Print out the name of each directory on its own line
print join("\n", @sub_dirs);
Octoberdan