1

I want to use sed to replace the entire line at a match with a bash variable. Replacing with plain text is fine.

$ echo -e "foo bar\nfooy bary\nfoot bart" | sed '/fooy/c\text'
foo bar
text
foot bart

If the text I want to replace with is stored in a bash variable, however, this does not work.

$ var="This is poetry."
$ echo -e "foo bar\nfooy bary\nfoot bart" | sed "/fooy/c\$var"
foo bar
$var
foot bart

How can I achieve this?

Krøllebølle
  • 2,878
  • 6
  • 54
  • 79
  • Remove the `\ ` It escapes the meaning of `$` – nu11p01n73R Jan 30 '15 at 09:00
  • Thanks. I got confused by all the examples I found, always using `c\ ` and not just `c`. Thought it was syntax. – Krøllebølle Jan 30 '15 at 09:01
  • If you would have researched a bit could have found the answer already. There are many such questions in SO for example http://stackoverflow.com/questions/7680504/sed-substitution-with-bash-variables. Anyways all the best !! – nu11p01n73R Jan 30 '15 at 09:03
  • The question is a bit different in my opinion, since I want to change the entire line and not use `sed "s/foo/bar/"` to only replace `foo` with `bar`. – Krøllebølle Jan 30 '15 at 09:05
  • Yeah I agree that it is different . But it clearly shows how you should be using bash variables in `sed`, since it was where you got stuck at – nu11p01n73R Jan 30 '15 at 09:07

2 Answers2

3

If you want to do this with sed (which you shouldn't, see below), use

sed "/fooy/c\\$var"

This is a shell expansion problem. \$ in a doubly quoted string means a literal dollar, so $var is not expanded. GNU sed also accepts

sed "/fooy/c$var"

as long as you don't specify --posix, but BSD sed (as you may have on FreeBSD or MacOS X) will complain about it.

However, doing this with sed is not a good idea. Substituting shell variables into sed code is almost never a good idea, because it makes you vulnerable to code injection. Take, for example,

$ var=$(echo -e 'something\nd')
$ echo -e "foo bar\nfooy bary\nfoot bart" | sed --posix "/fooy/c\\$var"
something

That wasn't what we expected, was it? What happens here is that sed, since it is unable do distinguish what you want it to treat as data ($var) and code, treats $var as code, and therefore it treats the d after the \n in it as a command (to delete the current line). A better way is to use awk:

$ var=$(echo -e 'something\nd')
$ echo -e "foo bar\nfooy bary\nfoot bart" | awk -v var="$var" '/fooy/ { print var; next } 1'
foo bar
something
d
foot bart

This sidesteps the code injection issue by not treating $var as code.

Wintermute
  • 42,983
  • 5
  • 77
  • 80
  • Thanks for a thorough explanation. I was not aware of the `'/pattern/ {...}'` syntax in `awk`. Will definitely start using that. – Krøllebølle Jan 30 '15 at 09:15
1

Don't escape the $ symbol. In bash, variables are referred by $variable-name. If you do escaping the $ symbol, then the bash won't consider it as a variable. It would treat $var as literal $var .

$ echo -e "foo bar\nfooy bary\nfoot bart" | sed "/fooy/c$var"
foo bar
This is poetry.
foot bart
Avinash Raj
  • 172,303
  • 28
  • 230
  • 274