Super User is a question and answer site for computer enthusiasts and power users. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I need to substitute some text inside a text file with a replacement. Usually I would do something like

sed -i 's/text/replacement/g' path/to/the/file

The problem is that both text and replacement are complex strings containing dashes, slashes, blackslashes, quotes and so on. If I escape all necessary characters inside text the thing becomes quickly unreadable. On the other hand I do not need the power of regular expressions: I just need to substitute the text literally.

Is there a way to do text substitution without using regular expressions with some bash command?

It would be rather trivial to write a script that does this, but I figure there should exist something already.

share|improve this question
    
Necessary to do it through bash? A simplistic solution would be to open in Word and do a find and replace all – Akash May 9 '12 at 15:04
11  
@akash Because systems that have bash always ship with Microsoft Word? ;) No.. Just kidding. The OP might want to do this on a remote machine or for a batch of files though. – slhck May 9 '12 at 15:07
    
@slhck :) Well, I guess gedit should have a similar option – Akash May 9 '12 at 15:09
    
An option would be to somehow correctly escape everything before passing it to sed, which is probably a futile effort considering all the switches and platform differences. – l0b0 May 9 '12 at 15:11
up vote 9 down vote accepted

When you don't need the power of regular expressions, don't use it. That is fine.
But, this is not really a regular expression.

sed 's|literal_pattern|replacement_string|g'

So, if / is your problem, use | and you don't need to escape the former.

ps: about the comments, also see this Stackoverflow answer on Escape a string for sed search pattern.


Update: If you are fine using Perl try it with \Q and \E like this,
perl -pe 's|\Qliteral_pattern\E|replacement_string|g'
RedGrittyBrick has also suggested a similar trick with stronger Perl syntax in a comment here

share|improve this answer
    
Thank you, I did not know about the difference between / and | – Andrea May 9 '12 at 15:18
22  
I'm not sure this answer is useful... The only difference between s||| and s/// is that the seperator character is different and so that one character doesn't need escaping. You could equally do s###. The real issue here is that the OP doesn't want to have to worry about escaping the contents of literal_pattern (which is not literal at all and will be interpreted as a regex). – Benj May 9 '12 at 15:31
4  
This will not avoid the interpretation of other special characters. What if search for 1234.*aaa with your solution it match much more than the intended 1234\.\*aaa. – Matteo May 9 '12 at 15:47

You could also use perl's \Q mechanism to "quote (disable) pattern metacharacters"

perl -pe 'BEGIN {$text = q{your */text/?goes"here"}} s/\Q$text\E/replacement/g'
share|improve this answer
2  
Or perl -pe 's(\Qyour */text/?goes"here")(replacement)' file – RedGrittyBrick May 9 '12 at 22:52
FIND='find this'
REPLACE='replace with this'
ruby -p -i -e "gsub(ENV['FIND'], ENV['REPLACE'])" path/to/file

This is the only 100% safe solution here, because:

  • It's a static substition, not a regexp, no need to escape anything (thus, superior to using sed)
  • It won't break if your string contains } char (thus, superior to a submitted Perl solution)
  • It won't break with any character, because ENV['FIND'] is used, not $FIND. With $FIND or your text inlined in Ruby code, you could hit a syntax error if your string contained an unescaped '.
share|improve this answer
    
I had to use export FIND='find this; export REPLACE='replace with this'; in my bash script so that ENV['FIND'] and ENV['replace'] had the expected values. I was replacing some really long encrypted strings in a file. This was just the ticket. – DMfll Jun 24 at 12:45

I pieced together a few other answers and came up with this:

function unregex {
   # This is a function because dealing with quotes is a pain.
   # http://stackoverflow.com/a/2705678/120999
   sed -e 's/[]\/()$*.^|[]/\\&/g' <<< "$1"
}
function fsed {
   local find=$(unregex "$1")
   local replace=$(unregex "$2")
   shift 2
   # sed -i is only supported in GNU sed.
   #sed -i "s/$find/$replace/g" "$@"
   perl -p -i -e "s/$find/$replace/g" "$@"
}
share|improve this answer

check out my Perl script. it do exactly what you need without implicit or explicit use of regular expression :

https://github.com/Samer-Al-iraqi/Linux-str_replace

str_replace Search Replace File # replace in File in place

STDIN | str_replace Search Replace # to STDOUT

very handy right? I had to learn Perl to do it. because I really really need it.

share|improve this answer

You can do it by escaping your patterns. Like this:

keyword_raw='1/2/3'
keyword_regexp="$(echo "$keyword_raw" | sed -e 's/[]\/$*.^|[]/\\&/g')"
# keyword_regexp is now '1\/2\/3'

replacement_raw='2/3/4'
replacement_regexp="$(echo "$replacement_raw" | sed -e 's/[\/&]/\\&/g')"
# replacement_regexp is now '2\/3\/4'

echo 'a/b/c/1/2/3/d/e/f' | sed -e "s/$keyword_regexp/$replacement_regexp/"
# the last command will print 'a/b/c/2/3/4/d/e/f'

Credits for this solutions goes here: https://stackoverflow.com/questions/407523/escape-a-string-for-a-sed-replace-pattern

Note1: this only works for non-empty keywords. Empty keywords are not accepted by sed (sed -e 's//replacement/').

Note2: unfortunately, I don't know a popular tool that would NOT use regexp-s to solve the problem. You can write such a tool in Rust or C, but it's not there by default.

share|improve this answer

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .