Dr. Faust
Dr. Faust

Reputation: 1021

How can I canonicalize Windows file paths in Perl?

Update:

I can make this a simpler problem to solve:

I want to figure out what the correct regex would be to substitute any single occurrence of a back slash with two back slashes.

I want to turn this:

vlc.plugin.path = C:\Program Files\JekyllV0.9.2\\VLC_1.0.0\\plugins

into:

vlc.plugin.path = C:\\Program Files\\JekyllV0.9.2\\VLC_1.0.0\\plugins

Original question:

I want to change the following in a file using a Perl Regex:

I tried the following:

perl" -p -i.orig -e "s#\\#\\\\#g" -e "s#/#\\\\#g" %VIDEOLOG_PROPERTIES_FILE%

where %VIDEOLOG_PROPERTIES_FILE% contains:

vlc.plugin.path = C:\Program Files\JekyllV0.9.2/VLC_1.0.0/plugins

Upvotes: 1

Views: 3406

Answers (5)

Sinan Ünür
Sinan Ünür

Reputation: 118166

You want File::Spec->canonpath.

Update:: Actually, that was the wrong recommendation. That will work for internal function calls etc but it will write single backslashes to the file. However, the following will work better:

#!/usr/bin/perl

use strict;
use warnings;

use Config::INI::Reader;

my $config = Config::INI::Reader->read_handle(\*DATA);

my $var1 = $config->{_}->{'vlc.plugin.path1'};
my $var2 = $config->{_}->{'vlc.plugin.path2'};

for my $v ($var1, $var2) {
    $v =~ s! (?: [\\]{1,2} ) | (?:/) !\\\\!gx;
    print "$v\n";
}

__DATA__
vlc.plugin.path1 = C:\Program Files\JekyllV0.9.2\\VLC_1.0.0\\plugins
vlc.plugin.path2 = C:\Program Files\JekyllV0.9.2/VLC_1.0.0/plugins

Output:

C:\\Program Files\\JekyllV0.9.2\\VLC_1.0.0\\plugins
C:\\Program Files\\JekyllV0.9.2\\VLC_1.0.0\\plugins

Upvotes: 4

chaos
chaos

Reputation: 124365

For the basic replace:

perl -p -i.orig -e "s#[/\\]#\\\\#g" %VIDEOLOG_PROPERTIES_FILE%

To replace only slashes and backslashes that do not appear adjacent to another instance of the same character:

perl -p -i.orig -e "s#(?<!/)/(?!/)#\\\\#g; s#(?<\\)\\(?!\\)#\\\\#g" %VIDEOLOG_PROPERTIES_FILE%

Upvotes: 6

Axeman
Axeman

Reputation: 29854

It doesn't work, because perl doesn't handle two -e flags--without a semicolon "between" the two commands. You have to write it as below (if you lose the d-quote right after 'perl', that is.)

perl -p -i.orig -e "s#\\#\\\\#g;" -e "s#/#\\\\#g" %VIDEOLOG_PROPERTIES_FILE%

I do something similar, but because Perl supports '/' on the PC, my preference is for forward slashes. So I use the following :

s![\\/]+!/!g;

Thus it can be easily turned around to

s![\\/]+!\\\\!g;

Now a word about why I do that: sometimes people can't figure out whether or not they should put a slash on the beginning or end of parts of paths that will be concatenated. At times you end up with even double forward slashes. (But not if you use File::Spec.) thus it's good to handle those kinds of collisions. Especially because it's going to be a path, we want to take however many slashes of whatever kind and turn them into the kind we like.

Additionally, I even do this:

s!([\\/]+([.][\\/]+)?)+!/!g

Because it captures those cases where it's the same cluster of slashes separated by a dot which does nothing, because path-wise / <=> (/+.)+ for those programs and scripts that handle the dots in path names, while the other programs will error out.

Upvotes: 3

name
name

Reputation: 5175

[21:09:00][mgrad@zuza-3:~]$ perl -pe 's#\/#\/\/#g; s#\\#\\\\#g' test.txt
vlc.plugin.path = C:\\Program Files\\JekyllV0.9.2//VLC_1.0.0//plugins

Upvotes: 0

Eevee
Eevee

Reputation: 48594

I'm pretty sure Perl blindly concatenates all its -e arguments, so those are being squashed into s#\\#\\\\#gs#/#\\\\#g, and then the second regex looks like a comment.

It works for me if I change it to -e 's#\\#\\\\#g; s#/#\\\\#g'.

Of course, you could do this with a single regex, since you're using the same replacement both times.

Upvotes: 3

Related Questions