0

I am trying to replace a sed command with a sed command and it keeps falling over so after a few hours of "picket fencing" I thought I would ask the question here.

I have various bash scripts that contain this kind of line:

 sed 's/a hard coded server name servername.//'

I would like to replace it with:

 sed "s/a hard coded server name $(hostname).//"

Note the addition of double quotes so that the $(hostname) is expanded which make this a little trickier than I expected.

So this was my first of many failed attempts:

cat file | sed 's!sed \'s\/a hard coded server name servername.\/\/\'!sed \"s\/a hard coded server name $(hostname).\/\/\"!g'

I also tried using sed's nice "-e" option to break down the replace into parts to try and target the problem areas. I wouldn't use the "-e" switch in a solution but it is useful sometimes for debugging:

cat file | sed -e 's!servername!\$\(hostname\)!' -e 's!\| sed \'s!\| sed \"s!'

The first sed works as expected (nothing fancy happening here) and the second fails so no point adding the third that would have to replace the closing double quote.

At this point my history descends into chaos so no point adding any more failed attempts.

I wanted to use the first replacement in a single command as the script is full of sed commands and I wanted to target just one specific command in the script.

Any ideas would be appreciated.

SnazzyBootMan
  • 669
  • 2
  • 15
  • 30
  • 2
    You can't escape single quotes inside single quotes you need to use double quotes on the outside or use `'\''` for every inner single quote. That said see if http://stackoverflow.com/q/29613304/258523 is of any help here. – Etan Reisner Mar 31 '16 at 13:38
  • 1
    Don't put the whole sed command in double quotes or you have to worry about other things sneaking in during future updates. Just break out of single quotes for the part you want the shell to expand: `sed 's/a hard coded server name '"$(hostname)"'.//'` – Ed Morton Mar 31 '16 at 13:43
  • I thought I had escaped the one single quote correctly in the command, could you expand @Etan ? – SnazzyBootMan Mar 31 '16 at 13:44
  • 1
    @EdMorton - brilliant, that works really well and as you mention is much more usable going forward! – SnazzyBootMan Mar 31 '16 at 13:50
  • wrt replacing the old sed command with a new one, I don't have time right now but if I were you I'd use awk instead of sed to do that since awk supports strings, not just regexps like sed uses, and it can treat `/`s as-is inside old/new strings. Also, you can use the octal `\047` to replace `'`s to make your escaping/quoting hell a bit more bearable. – Ed Morton Mar 31 '16 at 13:53
  • @EdMorton - I am not sure why I went with sed for doing this back in the day but I must have had a reason and now I have hundreds of them after the code has been copied and pasted many times in this environment. I am sticking with the sed for fear of breaking something so this seems to work very well - `cat file | sed -e 's!servername!'\'\"\$\(hostname\)\"\''!g'`. – SnazzyBootMan Mar 31 '16 at 14:02
  • You have `sed 's!sed \'s\/a hard coded` in the first failed attempt example and `sed \'s!\| ` in the second, etc. – Etan Reisner Mar 31 '16 at 14:38

2 Answers2

0

Follow the behavior of templating tools by using a sequence that should never appear in actual use and replace that. For example, using colons simply because they require less quoting:

#!/bin/bash
sed "s/:servername:/$(hostname)/g" <<EOF > my_new_script.bash
  echo "This is :servername:"
EOF

I've used echo in the internal script for purposes of clarity. You could have equally used something like:

sed 's/complex substitution :servername:/inside quotes :servername:/'

which avoids quoting hassles because the outer sed is treating the here document as plain text.

msw
  • 42,753
  • 9
  • 87
  • 112
0

Here's how you could do it in awk if you ignore (or handle) metachars in the old and new text like you would with sed:

$ awk -v old="sed 's/a hard coded server name servername.//'" \
      -v new="sed 's/a hard coded server name '\"\$(hostname)\"'.//' \
          '{sub(old,new)}1' file
sed 's/a hard coded server name '"$(hostname)"'.//'

or to avoid having to deal with metachars, use only strings for the comparison and replacement:

$ awk -v old="sed 's/a hard coded server name servername.//'" \
      -v new="sed 's/a hard coded server name '\"\$(hostname)\"'.//'" \
          's=index($0,old){$0=substr($0,1,s-1) new substr($0,s+length(old))}1' file
sed 's/a hard coded server name '"$(hostname)"'.//'
Ed Morton
  • 188,023
  • 17
  • 78
  • 185