tags:

views:

345

answers:

4

Is there some way to replace a string such as @or * or ? or & without needing to put a "\" before it?

Example:

perl -pe 'next if /^#/; s/\@d\&/new_value/ if /param5/' test

In this example I need to replace a @d& with new_value but the old value might contain any character, how do I escape only the characters that need to be escaped?

+5  A: 

As discussed at perldoc perlre:

...Today it is more common to use the quotemeta() function or the "\Q" metaquoting escape sequence to disable all metacharacters' special meanings like this:

/$unquoted\Q$quoted\E$unquoted/

Beware that if you put literal backslashes (those not inside interpolated variables) between "\Q" and "\E", double-quotish backslash interpolation may lead to confusing results. If you need to use literal backslashes within "\Q...\E", consult "Gory details of parsing quoted constructs" in perlop.

You can also use a ' as the delimiter in the s/// operation to make everything be parsed literally:

my $text = '@';
$text =~ s'@'1';
print $text;

In your example, you can do (note the single quotes):

perl -pe 's/\b\Q@f&\E\b/new_value/g if m/param5/ and not /^ *#/'
Ether
yael
about your last answer I dont work with perl scriptmy script is bash so I cant use perl syntax
yael
@yael: the last example was just that, an example. You can do anything with a perl oneliner that you can do with a real perl script, and similarly you can turn any perl oneliner into a real perl script (there's nothing wrong with short scripts!)
Ether
@yael: Your code snippet is not working because bash is parsing the backslashes before Perl ever sees them. Use single quotes rather than double -- bash treats them similarly to how Perl does.
Ether
Dave Hinton
Note that s''' is very clever but rather obscure and is probably something most Perl programmers have never seen.
Schwern
@Schwern: really? I see `m'specialchars'` all the time, as well as `s{foo}{bar}`.
Ether
+4  A: 

The other answers have covered the question, now here's your meta-problem: Leaning Toothpick Syndrome. Its when the delimiter and escapes start to blur together:

s/\/foo\/bar\\/\/bar\/baz/

The solution is to use a different delimiter. You can use just about anything, but balanced braces work best. Most editors can parse them and you generally don't have to worry about escaping.

s{/foo/bar\\}{/bar/baz}

Here's your regex with braced delimiters.

s{\@d\&}{new_value}

Much easier on the eyeholes.

Schwern
A: 

If you really want to avoid typing the \s, put your search string into a variable and then use that in your regex instead. You don't need quotemeta or \Q ... \E in that case. For example:

my $s = '@d&';
s/$s/new_value/g;

If you must use this in a one-liner, bear in mind that you will have to escape the $s if you use "s to contain your perl code, or escape the 's if you use 's to contain your perl code.

MkV
You probably meant to say: s/\Q$s\E/new_value/g;
Grant McLean
+1  A: 

You have several problems:

  1. You are using \b incorrectly
  2. You are replacing code with shell variables
  3. You need to quote metacharacters

From perldoc perlre

A word boundary ("\b") is a spot between two characters that has a "\w" on one side of it

Neither of the characters @ or & are \w characters. So your match is guaranteed to fail. You may want to use something like s/(^|\s)\@d\&(\s|$)/${1}new text$2/

(^|\s) says to match either the start of the string (^)or a whitespace character (\s).

(\s|$) says to match either the end of the string ($) or a whitespace character (\s).

To solve the second problem, you should use %ENV.

To solve the third problem, you should use the \Q and \E escape sequences to escape the value in $ENV{a}.

Putting it all together we get:

#!/bin/bash

export a='@d&'
export b='new text'

echo 'param5 @d&' | 
    perl -pe 'next if /^#/; s/(^|\s)\Q$ENV{a}\E(\s|$)/$1$ENV{b}$2/ if /param5/' 

Which prints

param5 new text
Chas. Owens
very niceplease explain the (^|\s)second I use file not by echo .....as perl -i -pe ........ fileneed to chage somthing?
yael
The `-p` option will take from STDIN or from a list of files passed in as arguments, so you should be fine using it the way you were before. I just used echo for demonstration purposes (I like self contained examples).
Chas. Owens
yael
@yael: try it and see. (The answer is yes.)
Ether