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

I have a directory like this:

/Volumes/Volume Name/

It contains a file named this:

/Volumes/Volume Name/A/B/filename

When I use File::Find to print out the file name, I am getting this:

/Volume Name//A/B/filename

Notice the two slashes after Volume Name. As a result, I am getting a no such directory error when I try to rename(move) filename. Here is my stripped down code:

use strict; use warnings; use 5.010; use File::Find; my $volume_name = "/Volumes/Volume Name/"; for (glob "$volume_name/*") { if (-d) { say $_; find(\&wanted, $_); } } sub wanted { say "****"; say $File::Find::name; say $File::Find::dir; say $_; say "****"; } --output:-- /Volumes/Volume Name//A/B/filename /Volumes/Volume Name//A/B filename

Replies are listed 'Best First'.
Re: File::Find problems
by jwkrahn (Abbot) on Jan 15, 2010 at 07:30 UTC
    my $volume_name = "/Volumes/Volume Name/"; for (glob "$volume_name/*") { if (-d) { say $_; find(\&wanted, $_); } }

    You have a trailing slash on the assignment to $volume_name and then you add another slash when you use $volume_name with glob, ergo, two slashes.

    You could try it like this:

    find \&wanted, grep -d, glob '/Volumes/Volume Name/*';
Re: File::Find problems
by ikegami (Patriarch) on Jan 15, 2010 at 07:55 UTC

    As a result, I am getting a no such directory error when I try to rename(move) filename.

    Not true. rename doesn't care about the doubled slash, because the system doesn't care about the doubled slash.

    $ mkdir -p x/x $ perl -e'rename "x//x", "x//y" or die' $ ls -l x total 4 drwx------ 2 ikegami pg1404028 4096 2010-01-14 23:55 y

    Also tested on Windows.

      Yes, I've decided I have NO IDEA how to deal with path names containing spaces. I also think the error given by rename() sucks: it doesn't tell you which path is faulty.

      I've performed "thousands" of tests, trying to figure out what is happening with my script to no avail. All I am trying to do is move some files from an external usb drive to a directory on my computer that I enter on the command line when prompted.

      I can't make heads or tails out of what's happening. When I print out $File::Find::name, I get this:

      /Volumes/Volume Name/A/B/filename
      That seems like a faulty path to me because the space in "Volume Name" is not escaped.

        That seems like a faulty path to me because the space in "Volume Name" is not escaped.

        Why would you expect something other than a path from File::Find? It was escaped, it wouldn't be a path.

        You need to escape something when you want to embed one thing into another. (A path in a glob pattern, a string in Perl code, a string in a command line, etc) How you escape it also dependent on what you want to embed it into. Once it's escaped, it's no longer a path. (It's a glob pattern, a Perl string literal, a shell string literal, etc)

        Yes, I've decided I have NO IDEA how to deal with path names containing spaces

        In a glob pattern? You've already been given two solutions. Escape the space or use bsd_glob.

        I also think the error given by rename() sucks: it doesn't tell you which path is faulty.

        It's not like you checking two paths is harder than checking one when you have a problem.

        The system simply returns a number which represents the error message.

        That seems like a faulty path to me because the space in "Volume Name" is not escaped.

        Why would you expect something other than a path from File::Find? It was escaped, it wouldn't be a path.

        You need to escape something when you want to embed one thing into another. (A path in a glob pattern, a string in Perl code, a string in a command line, etc) How you escape it also dependent on what you want to embed it into. Once it's escaped, it's no longer a path. (It's a glob pattern, a Perl string literal, a shell string literal, etc)

        Yes, I've decided I have NO IDEA how to deal with path names containing spaces

        In a glob pattern? You've already been given two solutions. Escape the space or use bsd_glob.

        I also think the error given by rename() sucks: it doesn't tell you which path is faulty.

        It's not like you checking two paths is harder than checking one when you have a problem.

        The system simply returns a number which represents the error message.

Re: File::Find problems
by 7stud (Deacon) on Jan 15, 2010 at 07:24 UTC

    Sheesh. The problem is here:

    my $volume_name = "/Volumes/Volume Name/"; for (glob "$volume_name/*")

    That ends up globbing for "/Volumes/Volume Name//*". I'm not sure how that successfully returns the files I am after, but that is screwing up the path in File::Find. Is there a function that joins pieces of paths, so as not to create that double slash?

      Hi

      Yes. You can avoid embedding the path separator by using File::Spec. Though for throwaway scripts I don't normally bother.

      I find the Functions version more convenient when not doing anything complicated with it:

      use strict; use warnings; use 5.010; use File::Spec::Functions qw(catfile rootdir); my $volume_name = catfile(rootdir, 'Volumes', 'Volume name'); say catfile($volume_name, '*');

      Hope this helps.

      FalseVinylShrub

      Disclaimer: Please review and test code, and use at your own risk... If I answer a question, I would like to hear if and how you solved your problem.

      "/" is a directory separator. Why do you have a trailing slash? It's not separating anything. Various modules will canonize paths, removing "./" and doubled separators from paths and trailing slashes, but that's just a cosmetic thing. The doubled slash will work perfectly fine.
Re: File::Find problems
by Anonymous Monk on Jan 15, 2010 at 08:57 UTC
    #!/usr/bin/perl -- use 5.010; use strict; use warnings; use File::Find::Rule; Main("/Volumes/Volume Name/"); #~ Main(@ARGV); exit(0); sub Main { my ($volume_name) = @_; my @dirs = File::Find::Rule->directory->maxdepth(1)->in($volume_name +); for my $dir (@dirs) { say $dir; for my $item ( File::Find::Rule->in($dir) ) { say "****"; say $item; say "****"; } } ## end for my $dir (@dirs) } ## end sub Main __END__