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

I'm trying to get a list of all the folders in a specific directory. I don't need anything fancy, no recursive scans or anything else. I just need all the directory names.

At work I've been given a list of almost 300 computer names and they want to see what version of Adobe Acrobat they have installed (we have a few different versions in our environment) so instead of doing it manually like they were expecting from me, I'd like to automate the task by reading everything in that folder.

This needs to work on other machines on my network meaning I'll be using c$. The following is what I tried.

open(NDIR, "\\pcname-vm\c$\\program files\adobe") or die "Error: $!"; my @folders = readdir(NDIR); close(NDIR); print @folders;
It dies saying "Unrecognized escale \p passed through at scanadobe.pl.."

Can someone help me get this work? I'm running Windows XP. </code>

Replies are listed 'Best First'.
Re: Quickest way to get a list of all folders in a directory
by ikegami (Patriarch) on Aug 13, 2009 at 14:51 UTC

    First, you're not building the string you intend to since "\" and "$" are special in double quoted string literals. You need to escape them with a "\". Didn't you get a file not found error? You should have verified what string you were passing to open.

    Second, you need opendir instead of open.

Re: Quickest way to get a list of all folders in a directory
by tokpela (Chaplain) on Aug 13, 2009 at 14:56 UTC

    Your backslashes in double quotes represents an escaped character. You could use single quotes:

    '\\pcname-vm\c$\program files\adobe'

    Or you could use double quotes with multiple escaping to compensate for the escaping:

    "\\\\pcname-vm\\c$\\program files\\adobe"

    Or, finally, you could use forward slashes:
    "//pcname-vm/c$/program files/adobe"

    Also, you should remove any non-directories from your list.

    use strict; use warnings; my $directory = "//pcname-vm/c$/program files/adobe"; open(NDIR, $directory) or die "Error: $!"; my @folders; while (my $filename = readdir(NDIR)) { # skip the . and .. directories next if ($filename =~ /^.+$/); my $folder = "$directory/$filename"; # skip if not a directory next if (! -d $folder); push(@folders, $filename); } close(NDIR); foreach (@folders) { print "$_\n"; }

      $/ is the input line separator, set to newline by default. $\ is the output record separator, set to undef by default. Several of these examples have not addressed these. It sounds like the OP is going to iterate over a list of system names and assemble the directory in parts. Single quotes and concatenation recommended:
      my $nodename = 'pcname-vm'; my $directory = '//'.$nodename.'/c$/program files/adobe';
      '\\pcname-vm\c$\program files\adobe'

      In a single-quoted string, '\' is special. See Quote Like Operators, which says:

      A backslash represents a backslash unless followed by the delimiter or another backslash, in which case the delimiter or backslash is interpolated.

      So, your single quoted string should be:

      '\\\\pcname-vm\c$\program files\adobe'
Re: Quickest way to get a list of all folders in a directory
by moritz (Cardinal) on Aug 13, 2009 at 14:52 UTC
    Use single quotes instead of double quotes ('\\pcname-vm\c$\\program files\adobe'). Note that '\\' produces just a single backslash, so you might need '\\\\'.
Re: Quickest way to get a list of all folders in a directory
by leocharre (Priest) on Aug 13, 2009 at 14:58 UTC
    You also have to test that what you're getting are really dirs.

    You misread the 'perldoc -f readdir', what they mean by next directory entry is not that the entry is a directory- but that the element is an entry in the directory you opened.

    I made my very silly directory subs.. may be of use.. LEOCHARRE::Dir. But really it's best to learn to do this.

    Note that topkela made an excellent suggestion.

    1. Isolate your directory path value my $directory = "//pcname-vm/c$/program files/adobe";
    2. Test that the value makes sense, that it's a dir, that it can be opened..
      -d $directory or die("Not dir: '$directory'"); opendir(DIR, $directory) or die("cant open '$directory', $!");
    3. Proceed to read the directory entries..
    (updated die(s) as ikegami pointed out)

      You also have to test that what you're getting are really dirs.

      No. opendir already does that. Checking it explicitly is redundant at best, and adds a race condition at worse.

      Isolate your directory path value

      That's a great idea since it allows you to print it in the error message, something you failed to do. This would have been extremely useful to the OP.

      $ perl -e' my ($dir_qn) = @ARGV; opendir(my $dh, $dir_qn) or die "Can'\''t open dir $dir_qn: $!\n"; ' somefile Can't open dir somefile: Not a directory

        Yes- I was thinking here about the person reading the code more than the code- it would make it extra special with sugar on top obvious about what the problem is or is not.. And.. if the OP is not even checking that the entries are files or dirs or what- then seeing a line that tells -d or x.. might be needed? For the human, that is.

        But you're right, very good point, opendir() already checks that it's a dir.

Re: Quickest way to get a list of all folders in a directory (slurp)
by toolic (Bishop) on Aug 13, 2009 at 19:26 UTC
    I just need all the directory names.
    Here is yet another portable way to do it, using the read_dir function from the File::Slurp CPAN module to get a directory listing, then selecting only directory names using grep and -d.

    read_dir automatically filters out the specially-named dot directories (. and ..):

    use strict; use warnings; use Data::Dumper; use File::Slurp; my $dir = '/path/to/some/dir'; my @folders = grep { -d "$dir/$_" } read_dir($dir); print Dumper(\@folders);

    I realize this does not solve your funky path name problem (but others have given you solutions for that), and I do not know if this is the 'Quickest way', but it is an alternative to opendir/readdir.

why not glob
by Boldra (Curate) on Aug 14, 2009 at 05:55 UTC
    I don't know why I see glob so rarely recommended in the monastary. I understand earlier perl versions had some problems on some OSs, but it's fine now (isn't it?)
    my @folders = glob('//pcname-vm/c$/program files/adobe/*');
    Looks much cleaner, doesn't it? And probably you weren't interested in all of the files in the adobe directory anyway.

    I do a bit of this sort of work, here are some other things that might be interesting:
    • 'c:/program files' is language dependant. eg here in Germany it's 'c:/programme'
    • Win32::TieRegistry works across networks, you might find it easier and quicker, particularly for finding versions of installed software.
    • This kind of task is called 'software inventory'. There are commercial products available.

    UPdate: correction in code


    - Boldra
      For directories only you want
      grep -d, glob ...
      I don't use glob because it has its own syntaxt and quirks, I find this much clearer to use File::Find::Rule with regex
      #!/usr/bin/perl -- use strict; use warnings; use File::Find::Rule; my $dir = '//pcnamevm/c$/program files/adobe/'; $dir = 'C:/program files/Mozilla Firefox/'; my @folders = File::Find::Rule->directory->maxdepth(1)->in($dir); use DDS; Dump \@folders; __END__ $ARRAY1 = [ 'C:/program files/Mozilla Firefox', 'C:/program files/Mozilla Firefox/chrome', 'C:/program files/Mozilla Firefox/components', 'C:/program files/Mozilla Firefox/defaults', 'C:/program files/Mozilla Firefox/dictionaries', 'C:/program files/Mozilla Firefox/extensions', 'C:/program files/Mozilla Firefox/greprefs', 'C:/program files/Mozilla Firefox/modules', 'C:/program files/Mozilla Firefox/plugins', 'C:/program files/Mozilla Firefox/res', 'C:/program files/Mozilla Firefox/searchplugins', 'C:/program files/Mozilla Firefox/uninstall' ];
        One difference between glob and File::Find::Rule is that the latter also returns the parent directory ('C:/program files/Mozilla Firefox', in your example). It seems the OP does not want that.
Re: Quickest way to get a list of all folders in a directory
by Clarendon4 (Acolyte) on Aug 17, 2009 at 08:59 UTC

    If you are interested in the actual '*quickest* way to get a list of all folders in a directory' on Windows then you'll need either an XS extension or external C program *.

    Why? Because the pure Perl code typically used to do this task of opendir/readdir/-d will make unnecesary extra system calls. At the Win32 API level Perl's readdir is done by FindNextFile() which returns a WIN32_FIND_DATA structure that contains whether the returned file is a directory. Unfortunately this info is not kept so you must then stat() it to test if it's a directory. This extra work can amount to a significant overhead.

    And also remember to:  ${^WIN32_SLOPPY_STAT} = 1; to avoid more extraneous system calls (unless you are actually interested in the link count of stat'd files!)

    * I use qfind to efficiently find all the files in a directory. Modifying it to do what you want is left as an exercise :-) You can find it at http://backpan.perl.org/authors/id/A/AD/ADAVIES/qfind.c-1.06

    alex.

Re: Quickest way to get a list of all folders in a directory
by budoka (Initiate) on Aug 13, 2009 at 14:48 UTC
    flip the slashes
      Nah dude, the OP is doign these on OTHER machines. Make the slashies into / will make it look locally on his/her machine and not the target's.

        Not true. / and \ are interchangeable in Windows. Both of the following strings are acceptable:

        • \\pcname-vm\c$\program files\adobe
        • //pcname-vm/c$/program files/adobe

        (You can mix and match too)

        All of the following Perl string literals create one of those strings:

        • "\\\\pcname-vm\\c\$\\program files\\adobe"
        • "//pcname-vm/c\$/program files/adobe"
        • '\\\\pcname-vm\\c$\\program files\\adobe'
        • '\\\pcname-vm\c$\program files\adobe'
        • '//pcname-vm/c$/program files/adobe'
Re: Quickest way to get a list of all folders in a directory
by toolic (Bishop) on Aug 15, 2009 at 18:21 UTC
Re: Quickest way to get a list of all folders in a directory
by ack (Deacon) on Aug 15, 2009 at 02:39 UTC

    All of the responses to the OP's inquiry are excellent and I've learned a lot about some of more subtle capabilities and actions of Perl.

    One curious thing in the OP's post: The OP use an open() rather than and opendir(). I thought you had to do an opendir() to be able to use readdir(). Am I mistaken?

    ack Albuquerque, NM
      I thought you had to do an opendir() to be able to use readdir().
      You are correct. Read the links that ikegami mentioned.