3

I am not able to take care of special characters.

I have the following perl script.

while(@mapping_array[$i])
{  
  chomp(@mapping_array[$i]);
  my @core= split ( /  / , $mapping_array[$i]) ;
  @core[0] =~ tr/ //ds ;   ## Deleting blank spaces
  @core[1] =~ tr/ //ds ;     
  system("perl -pi -e 's/@core[0]/@core[1]/' $testproc ");  
  print "@core[0] \n";
  print "@core[1] \n";
  $i++;
}

The issue is that my @core[0] variable could be a simple string like abc or a more complex one like TEST[1]. My script works as expected for abc, replacing it with the value of @core[1], but it failes if my @core[0] is TEST[1].

Using ? instead of / in the substitution operator doesn't help. How can I do this correctly?

  • 1
    Array elements use the singular sigil: $core[0]. – choroba Oct 21 '15 at 10:05
6

Sounds like you're looking for quotemeta. As explained in perldoc -f quotemeta:

quotemeta EXPR
        Returns the value of EXPR with all the ASCII non-"word" characters
        backslashed. (That is, all ASCII characters not matching
        "/[A-Za-z_0-9]/" will be preceded by a backslash in the returned
        string, regardless of any locale settings.) This is the internal
        function implementing the "\Q" escape in double-quoted strings.

So, your script would be (note that array elements should be specified as $foo[N], not @foo[N]):

chomp(@mapping_array);
while($mapping_array[$i])
{  
    my @core= split ( /  / , $mapping_array[$i]) ;
    $core[0] =~ tr/ //ds ;   ## // Deleting blank spaces
    $core[1] =~ tr/ //ds ;   # / fix SO highlighting
    my($k,$l)=(quotemeta($core[0]),quotemeta($core[1]))
    system("perl -pi -e 's/$k/$l/' $testproc "); 
    print "$core[0] \n$core[1] \n";
    $i++;
}
  • I was really looking for 'quotemeta'. Yes, array elements should be specified as $foo[N] and not @foo[N], I could not really figure out the difference between the two.It will be helpful if you can point out the differences. – Ramneek Singh Kakkar Oct 21 '15 at 10:34
  • Thanks for including the actual quotemeta definition from the documentation. +1 for that. – Wildcard Oct 21 '15 at 10:36
  • 1
    @user3687023 $foo[n] is the nth element of array @foo, and @foo[n] is an array slice, it's a new array with one element. Try running perl -MO=Deparse,-q -le '@F=("foo"); print "@F[0]"'. You will see that @F[0] is actually join($", @F[0]). This sort of thing is why it's always a good idea to use warnings;. The code above produces this warning: Scalar value @F[0] better written as $F[0] at -e line 1.. – terdon Oct 21 '15 at 10:41
  • In perl the "sigil" ($, @, % etc.) denotes what data type you expect out of the thing. For a single scalar therefore, it's always $ - $value, $array[$index] or $hash{$key}. Perl knows what it is, because of the type of bracket you're using (if any), not by the sigil. It's perfectly valid to write @array[1..3] (because you're after multiple scalars). Or @hash{("key1", "key2", "key3")} to get a list of values matching the designated keys. – Sobrique Oct 21 '15 at 11:44
5

Running Perl from Perl can usually be avoided.

for my $both (@mapping) {
    my ($regex, $replace) = split /  /, $both;
    tr/ //ds for $regex, $replace;                                                                   # // Fix SO highlighting bug.

    open my $IN,  '<', $testproc or die $!;
    open my $OUT, '>', "$testproc.new" or die $!;
    while (<$IN>) {
        s/\Q$regex/$replace/;
        print {$OUT} $_;
    }
    close $OUT or die $!;
    rename $testproc, "$testproc.old" or die $!;
    rename "$testproc.new", $testproc or die $!;
}

The \Q corresponds to quotemeta which prevents interpretation of special characters in the $regex variable.

4

First off - turn on strict and warnings at the top of your program:

use strict;
use warnings;

This will help you identify errors like @core[0] isn't actually right.

However the second problem is - you're sending meta characters to your regex - [] means something special in regex.

So what you really need is the quotemeta function.

print quotemeta '@core[0]';

Which turns it into:

\@core\[0\]

Or

print quotemeta $core[0]; 

Which in the example you give will print:

TEST\[1\]

Of course, you also probably don't need to system() call perl from within perl. That's just inefficient and messy.

2

Use \Q in the regexp to remove special meaning from chars:

system("perl -pi -e 's/\\Q$core[0]/$core[1]/' $testproc ");

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.