Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I am using perl to colorize a matched string

MATCH=aaa
printf "aaa\n/dev/aaa\nccc\n" | perl -pe "s/($MATCH)/\e[1;31m\1\e[0m/g"

This works well until I try to match a string containing special characters, e.g.

MATCH=/dev/
printf "aaa\n/dev/aaa\nccc\n" | perl -pe "s/($MATCH)/\e[1;31m\1\e[0m/g"

The reason is, when bash substitutes $MATCH, perl gets the following expression:

perl -pe "s/(/dev/)/\e[1;31m\1\e[0m/g"

I would need to backslash those / to make it work, i.e.

MATCH=\/dev\/
printf "aaa\n/dev/aaa\nccc\n" | perl -pe "s/($MATCH)/\e[1;31m\1\e[0m/g"    

But I don't know what MATCH will contain. It could be anything, + or (. Is there a way to tell perl to treat the characters literally, not as an expression?

UPDATE:

Using solution suggested by Joseph R. gives following error:

MATCH=/dev/
printf "aaa\n/dev/aaa\nccc\n" | perl -pe "s/\Q($MATCH)\E/\e[1;31m\1\e[0m/g"
Backslash found where operator expected at -e line 1, near ")\"
(Missing operator before \?)
Having no space between pattern and following word is deprecated at -e line 1.
syntax error at -e line 1, near "s/\Q(/dev/)"
Search pattern not terminated at -e line 1.

UPDATE2:

now I don't get any error, but there seems to be no match (there is no coloring):

# MATCH=/dev/ ; printf "aaa\n/dev/aaa\nccc\n" | perl -pe 's/(\Q$ENV{MATCH}\E)/\e[1;31m\1\e[0m/g'
aaa
/dev/aaa
ccc
share|improve this question

1 Answer 1

up vote 2 down vote accepted

Yes, you could use Perl's quotemeta operator with single quotes:

export MATCH=...
... | perl -pe 's/(\Q$ENV{MATCH}\E)/...

Anything between \Q and \E (or till the end of the regex if no \E is supplied) is considered as-is with no special meaning attached to metacharacters.

Equivalently:

export MATCH=...
... | perl -pe '$sane=quotemeta $ENV{MATCH};s/($sane)/...

Update

Here's how it can be done without export:

MATCH=/dev/
printf "aaa\n/dev/aaa\nccc\n" \
 | perl -pe "\$sane=quotemeta q{$MATCH};s/(\$sane)/\e[1;31m\$1\e[0m/g"

Note that your code had a mistake. You can't use a backreference (\1) in the replacement pattern of a s///, you need to use the match variable instead ($1), which I have used as \$1 here, to avoid the shell interpolating $1 because of the soft quotes.

In general, if you're doing a lot of colorizing work with Perl, I would suggest using a module to handle terminal idiosyncrasies for you. Check out Term::ANSIColor for example.

share|improve this answer
    
I get an error. Please see my updated question. –  Martin Vegter May 25 at 22:04
    
@MartinVegter Sorry, my bad. The quotemeta tricks works with interpolated variables only. Please see the updated answer. –  Joseph R. May 25 at 22:14
    
please see my update2 –  Martin Vegter May 25 at 22:20
    
@MartinVegter As I put in my edit, you need to export MATCH for it to be visible from within the Perl sub-process. In the previous case, the current shell was handling the substitution so it was able to see MATCH without an export. –  Joseph R. May 25 at 22:21
1  
@MartinVegter Make sure you're not including the | inside your quotemeta, otherwise it loses its meaning. –  Joseph R. Jun 1 at 18:11

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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