views:

586

answers:

5

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:

  • all occurrences of a single back slash to two back slashes
  • all occurrences of a single forward slash to two back slashes

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
+6  A: 

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%
chaos
Can you explain why that would work differently from what Faust already tried?
Rob Kennedy
@Rob: well, to begin with, it doesn't produce a syntax error. As Eevee says in his answer, you could fix that by adding a semi-colon to the first substitution. Chaos makes the two regexes simpler by combining them in the character class.
Telemachus
When I use what you suggested I get: vlc.plugin.path = C:\\Program Files\\JekyllV0.9.2\\\\VLC_1.0.0\\\\pluginsIs there a way to avoid changing the existing "\\" to "\\\\"?
Dr. Faust
Yeah, edited for solution that will only replace lone instances.
chaos
+3  A: 

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.

Eevee
A: 
[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
name
+3  A: 

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.

Axeman
This isn't really right. Perl handles two `-e` clauses just fine; what it doesn't do is insert an implicit semicolon between them.
chaos
@chaos You are *absolutely* right, color me surprised that I've got to tell Perl to put a command separator there. I've always gone with the functional assumption that it doesn't work. But I tried it and it worked on all the platforms I tried. Thanks!
Axeman
+4  A: 

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
Sinan Ünür
Thanks. This was very instructive and useful.
Dr. Faust