Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

Any way to simulate a Windows path handling for File::Spec without Windows?

by nysus (Parson)
on Sep 08, 2019 at 04:56 UTC ( [id://11105817]=perlquestion: print w/replies, xml ) Need Help??

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

Got the following sub for creating what I call "short paths," which are relative file paths that are relative to a common directory shared by a group of arbitrary files. It's broken in Windows. I'd rather avoid having to use Windows to test it. Is there a way I can tell File::Spec to behave as if it's on a Windows machine?

use strict; use warnings; use File::Spec; my @files = qw ( C:\STRAWB~1\cpan\build\File-Collector-0.024-0\t\test_data\many_files\ +dir1\file4 C:\STRAWB~1\cpan\build\File-Collector-0.024-0\t\test_data\many_files\ +dir1\file5 C:\STRAWB~1\cpan\build\File-Collector-0.024-0\t\test_data\many_files\ +dir1\file6 C:\STRAWB~1\cpan\build\File-Collector-0.024-0\t\test_data\many_files\ +dir2\file7 C:\STRAWB~1\cpan\build\File-Collector-0.024-0\t\test_data\many_files\ +dir2\file8 C:\STRAWB~1\cpan\build\File-Collector-0.024-0\t\test_data\many_files\ +dir2\file9 C:\STRAWB~1\cpan\build\File-Collector-0.024-0\t\test_data\many_files\ +file1 C:\STRAWB~1\cpan\build\File-Collector-0.024-0\t\test_data\many_files\ +file2 C:\STRAWB~1\cpan\build\File-Collector-0.024-0\t\test_data\many_files\ +file3 ); &do(@files); sub do { my @files = @_; my $os = $^O; my $file = pop @files; my ($vol, $dirs) = File::Spec->splitpath( $file ); my @comps = File::Spec->splitdir( $dirs ); my ($new_string, $longest_string) = ''; foreach my $cfile (@files) { my ($cvol, $cdirs) = File::Spec->splitpath( $cfile ); my @ccomps = File::Spec->splitdir($cfile); my $lc = 0; foreach my $comp (@ccomps) { if (defined $comps[$lc] && $ccomps[$lc] eq $comps[$lc]) { $new_string .= File::Spec->catfile($ccomps[$lc++], ''); next; } $longest_string = $new_string; ($cvol, $cdirs) = File::Spec->splitpath( $new_string ); @comps = File::Spec->splitdir($cdirs); $new_string = ''; last; } } # $s->{_common_dir} = $longest_string || (fileparse($file))[1]; if (@files) { foreach my $file ( @files, $file ) { my $tfile = $file; $tfile =~ s/$longest_string//; # $s->{_files}{all}{$file}{short_path} = $tfile; } } else { # $s->{_files}{all}{$file}{short_path} = $file; } }

$PM = "Perl Monk's";
$MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate Priest Vicar";
$nysus = $PM . ' ' . $MCF;
Click here if you love Perl Monks

  • Comment on Any way to simulate a Windows path handling for File::Spec without Windows?
  • Download Code

Replies are listed 'Best First'.
Re: Any way to simulate a Windows path handling for File::Spec without Windows?
by jcb (Parson) on Sep 08, 2019 at 05:48 UTC

    Your code does not work on Windows because you are misusing File::Spec. File::Spec->splitpath returns three items, not two. On *nix, the filename can end up with the directory in the middle, which is why your code appears to work in that case. Read the File::Spec documentation very carefully, and I think you will be able to fix this easily.

    Also, you are splitting the directory component, but then using catfile instead of catdir to reassemble those names. Think very carefully about types and what your strings mean, and I think that you will find a solution.

      I originally had it split with the volume as a separate return value. See original code. But it turns out I don't need it. splitdir includes the volume in the returned array, which works for me.

      catfile was needed on *unix to generate the slash when used with an empty string (but it does not work like that on Windows).

      $PM = "Perl Monk's";
      $MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate Priest Vicar";
      $nysus = $PM . ' ' . $MCF;
      Click here if you love Perl Monks

        That will work on POSIX, where the volume is null, and on Windows, where the volume is syntactically equivalent to a leading directory name. If I recall correctly, that will not work on VMS, where the filename syntax clearly distinguishes between volume, directories, and (leaf) filename, and there may be other systems where that also breaks. It is best to go all the way with File::Spec if you can.

        Of course, if you do not care about portability to VMS, that will not be a problem. :-)

Re: Any way to simulate a Windows path handling for File::Spec without Windows?
by davido (Cardinal) on Sep 08, 2019 at 20:38 UTC

    This isn't the point to your post but I wanted to mention a possible bug I noticed:

    my ($new_string, $longest_string) = '';

    This probably ought to be my ($new_string, $longest_string) = ('','');, because later you're using $longest_string as a pattern with the s/$longest_string// construct. But being undefined is the least of your worries:

    #!usr/bin/env perl use strict; use warnings; use File::Temp qw(tempdir); use File::Spec::Functions qw(catfile); my $dir = tempdir('pmtest_XXXXX', TMPDIR => 1, CLEANUP => 1); my $test_filename = '{a}'; my $full_path = catfile($dir, $test_filename); open my $fh, '>', $full_path or die "Cannot create $full_path: $!\n"; print $fh "Hello world.\n"; close $fh; print "We created $full_path as a file.\n" if -e $full_path && -f _; my $target = "$full_path.extra"; $target =~ s/$full_path//;

    ...produces...

    We created /tmp/pmtest_hmfIn/{a} as a file. Unescaped left brace in regex is passed through in regex; marked by <- +- HERE in m//tmp/pmtest_hmfIn/{ <-- HERE a}/ at mytest.pl line 23.

    If you use a part of a path as a regular expression pattern that pattern has all the semantics of a regular expression pattern. You probably want to use quotemeta or \Q, to play it a little safer. Otherwise, you're exposing the regex engine to user input, which should be considered hostile. One could create a path that results in a pattern that either fails to parse, or that parses but has abysmal performance.


    Dave

      Thanks, yeah, I caught the regex bug and fixed it in a release earlier today. Thanks for the not on multiple assignment. I'll fix that up.

      $PM = "Perl Monk's";
      $MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate Priest Vicar";
      $nysus = $PM . ' ' . $MCF;
      Click here if you love Perl Monks

Re: Any way to simulate a Windows path handling for File::Spec without Windows?
by nysus (Parson) on Sep 08, 2019 at 05:12 UTC

    NVM, looks like I can use File::Spec::Win32

    $PM = "Perl Monk's";
    $MCF = "Most Clueless Friar Abbot Bishop Pontiff Deacon Curate Priest Vicar";
    $nysus = $PM . ' ' . $MCF;
    Click here if you love Perl Monks

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://11105817]
Approved by jcb
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others scrutinizing the Monastery: (4)
As of 2024-04-25 06:38 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found