Why is my perl replace not working?

2019-09-10 07:07发布

问题:

I have the following perl search and replace :

With Escapes :

perl -pi -w -e 's/ onclick=\"img=document.getElementById(\'img_1\')\; img.style.display = (img.style.display == \'none\' ? \'block\' : \'none\');return false"//' test.html 

Without Escapes :

perl -pi -w -e 's/ onclick="img=document.getElementById('img_1'); img.style.display = (img.style.display == 'none' ? 'block' : 'none');return false"//' test.html 

My objective is to replace onclick="img=document.getElementById('img_1'); img.style.display = (img.style.display == 'none' ? 'block' : 'none');return false" with nothing in the file test.html. What am I messing up?

I keep on getting the error : -sh: syntax error near unexpected token)'` which I cannot help but feel is because of some stupid escaping on my part. Please help me out.

回答1:

[ You didn't specify for which shell you are building the command. I'm going to assume sh or bash. ]


Problem #1: Converting text into a regex pattern

Many characters have a special meaning in regex patterns. Among those you used, (, ), . and ? are special. These need to be escaped.

If you want to match

onclick="img=document.getElementById('img_1'); img.style.display = (img.style.display == 'none' ? 'block' : 'none');return false"

You need to use the pattern

onclick="img=document\.getElementById\('img_1'\); img\.style\.display = \(img\.style\.display == 'none' \? 'block' : 'none'\);return false"

So your Perl code is

s/onclick="img=document\.getElementById\('img_1'\); img\.style\.display = \(img\.style\.display == 'none' \? 'block' : 'none'\);return false"//

Problem #2: Converting text (a Perl program) into a shell literal

This is the problem you asked about.

Sections quoted by single quotes ("'") end at the next single quote (unconditionally), so the following is wrong:

echo 'foo\'bar'   # Third "'" isn't matched.

If you wanted to output "foo'bar", you could use

echo 'foo'\''bar'   # Concatenation of "foo", "'" and "bar".

foo and bar are quoted by single quotes, and the single quote is escaped using \. \ works here because it's outside of single quotes.

So, the lesson is basically use '\'' instead of ' inside single quotes.

you want to pass the following program to Perl:

s/onclick="img=document\.getElementById\('img_1'\); img\.style\.display = \(img\.style\.display == 'none' \? 'block' : 'none'\);return false"//

To do so, you'll need to create a shell literal that produces the correct argument, meaning we want to surround the whole with single quotes and escape the existing single quotes. We get the following:

's/onclick="img=document\.getElementById\('\''img_1'\''\); img\.style\.display = \(img\.style\.display == '\''none'\'' \? '\''block'\'' : '\''none'\''\);return false"//'

As such, the entire command would be

perl -i -wpe's/onclick="img=document\.getElementById\('\''img_1'\''\); img\.style\.display = \(img\.style\.display == '\''none'\'' \? '\''block'\'' : '\''none'\''\);return false"//' test.html


回答2:

Using the next:

perl -lnE 'say quotemeta $_'

and feed it with your plain input:

onclick="img=document.getElementById('img_1'); img.style.display = (img.style.display == 'none' ? 'block' : 'none');return false" 

you will get:

onclick\=\"img\=document\.getElementById\(\'img_1\'\)\;\ img\.style\.display\ \=\ \(img\.style\.display\ \=\=\ \'none\'\ \?\ \'block\'\ \:\ \'none\'\)\;return\ false\"

So using it:

perl -i -pe "s/onclick\=\"img\=document\.getElementById\(\'img_1\'\)\;\ img\.style\.display\ \=\ \(img\.style\.display\ \=\=\ \'none\'\ \?\ \'block\'\ \:\ \'none\'\)\;return\ false\"//"

should work.



回答3:

Some things are easier when you don't use a one-liner. Less things to escape, and not as fragile.

use strict;
use warnings;

my $literal_string = q{ onclick="img=document.getElementById('img_1'); img.style.display = (img.style.display == 'none' ? 'block' : 'none');return false"};

while (<>) {
    s/\Q$literal_string//;
    print;
}

Then execute the script:

perl -i thescript.pl test.html