1

I have 3 lines of numbers stored in a variable $new and I need to use this to replace 3 lines from a file which I have used grep and stored in another variable $old. I'm trying sed "s/$old/$new/g" input > output. But I'm getting an error :

sed: 1: "s/\( 28.47969107 0. ...": unterminated substitute pattern

My input file looks like:

scale
 1.000
primitive lattice vectors
    28.47969107  0.00000000  0.00000000
   0.00000000 28.47969107  0.00000000
   0.00000000  0.00000000 28.47969107
grid dimensions
  100 100 100

My $old is the lines

28.47969107 0.00000000 0.00000000 0.00000000 28.47969107 0.00000000 0.00000000 0.00000000 28.47969107

$new has the same format as old, just different numbers.

  • sed might be the wrong tool for this. Show samples of input, old, new, and your desired output. – John1024 Feb 18 '15 at 23:22
  • My old and new have the same format such as below: 28.8737008089 0.1964626186 0.0087346254 0.1957460913 29.4858745413 -0.1425364811 0.0094444937 -0.1406789200 28.4825428592 The input is a long text file. – Ajax Feb 18 '15 at 23:24
  • That is not helpful. To be helpful, show samples of input, old, new, and your desired output. – John1024 Feb 18 '15 at 23:26
  • My input has: <some lines of text> 28.47969107 0.00000000 0.00000000 0.00000000 28.47969107 0.00000000 0.00000000 0.00000000 28.47969107 <more lines of text> I need just the three lines to be replaced. – Ajax Feb 18 '15 at 23:28
  • Ajax, don't put the information in comments. Comments mangle the format. Put it in the question where you have full editing capability and make sure that it appears as it should. – John1024 Feb 18 '15 at 23:31
0
sed -e "/$old/i$new" -e "/$old/{N;N;d}" input > output

where

  • $old can be just 28.47969107 or ONE first line 28.47969107 0.00000000 0.00000000
  • $new SHOULD be in form 28.47969107 0.00000000 0.00000000\n0.00000000 28.47969107 0.00000000\n0.00000000 0.00000000 28.47969107

Other way (if you have new GNU sed (version >4.2.1) with -z option) to escape newline inside the vaiables:

sed -z "s/$(printf "%b" "$old"|sed '$!s/$/\\/')/$(printf "%b" "$new"|sed 's/$/\\/')/" input > output

Or if you do substitution inside the script assign function

end_esc(){ printf "%b" "$@"|sed '$!s/$/\\/' ; }
sed -z "s/$(end_esc $old)/$(end_esc $new)/" input > output
  • I still get the same error: sed: 1: "/ 28.47969107 0.000 ...": unterminated regular expression – Ajax Feb 18 '15 at 23:51
  • insert needs continuing newlines to be backslash escaped - and you can't have newlines in the address. Probably $IFS takes care of that here, but that's only because $old should be quoted. – mikeserv Feb 19 '15 at 9:38
  • @mikeserv I have stated it in requirements to variables below script – Costas Feb 19 '15 at 9:48
  • How does it match the other two lines? And the \n escape is not portable in the rhs - it will do funny things depending on the sed. Oh - especially for insert - most seds need literal newlines backslash escaped - and the inserted content doesn't actually start until after the first backslash escaped newline. Even GNU sed requires at least one backslash following the i. – mikeserv Feb 19 '15 at 10:20
0

First, let's define a sample string for new:

$ new=$'1 2 3\n4 5 6\n7 8 9'

Now, let's remove the three lines that follow primitive lattice vectors and replace them with new:

$ awk -v "new=$new" 'f==1{print new} f{f--;next} {print} /primitive/{f=3}' input
scale
 1.000
primitive lattice vectors
1 2 3
4 5 6
7 8 9
grid dimensions
  100 100 100
0
sed -e '$!N;$b' -e "/\n.*\n/!N;s/$old/$new/;t" -e P\;D

The above will work, but here's the catch: newlines in $old need to be represented as \n and newlines in $new should be preceded by a \backslash. The newlines in your values are what gave you trouble. For example, after expansion:

sed 's/24.0000000001
.000000000000
.0000000000/...'

...doesn't work. That is an unterminated regular expression - you see?

Instead:

old='num\nnum\nnum'
new='num\
num\
num'

...is what you need.

If, for whatever reason, you find it cumbersome to escape the vars yourself, then you can do something like:

(  set -f; IFS='
'; printf '%s\\n' / .* '/!N;s/'$old
   printf '%s\\\n' */$new"/;t$IFS#"
)| sed -e '$N;$b' -f - -e P\;D input > output

But that would depend on there being no blank lines in either variable, and it probably isn't any easier.

-1

sed s/"$old"/"$new"/g works for me

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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