lolololol ol
lolololol ol

Reputation: 868

Replace line with multi-line file

Trying to replace a line with a multi-line file. I can easily do this with a single-line file or multi-line string (see below).

#!/bin/bash

NEW_STRING="apple\nbanana\ncarrot"

sed -i "3s/.*/$(echo "${NEW_STRING}")/" tmp.txt

# Outputs...
# line 1
# line 2
# apple
# banana
# carrot
# line 4
# line 5
# ...
# etc

However, when I change the code to use a multi-file, such as replace.txt, I receive the following error:

sed: -e expression #1, char 10: unterminated `s' command

broken_script.bash

#!/bin/bash

FILE=`cat replace.txt`

sed -i "3s/.*/$(echo "${FILE}")/" tmp.txt

replace.txt

++++
++++++++++++++++++++++++++++++++++++++++++++++
  apple size=8, align=2, ..., etc.
  banana size=64, align=8, ..., etc.
  ...
  carrot size=92, align=4, ..., etc.

Note broken_script.bash works if I delete replace.txt to be a single-line (i.e. just ++++).

Does anyone see what I'm doing wrong? Why doesn't this work with a multi-line file as the replacement text (i.e. like the single-line file or multi-line string)?

Upvotes: 0

Views: 178

Answers (3)

William Pursell
William Pursell

Reputation: 212198

To replace lines with the contents of a file, you can use the r command:

sed -e 3rreplace.txt -e 3d tmp.txt

As asked in a comment, sed -e 3d -e 3rreplace.txt does not work because the d command immediately returns to the top of the program after reading the next line and never executes the r command.

Upvotes: 2

karakfa
karakfa

Reputation: 67467

sed can do this with some quote juggling

$ seq 5 | sed -e '/3/{r replace.txt' -e 'd}'

1
2
++++
++++++++++++++++++++++++++++++++++++++++++++++
  apple size=8, align=2, ..., etc.
  banana size=64, align=8, ..., etc.
  ...
  carrot size=92, align=4, ..., etc.
4
5

for in place replacement you need to provide file

$ sed -i -e '/3/{r replace.txt' -e 'd}' file

of course test first or take backup.

Upvotes: 1

Socowi
Socowi

Reputation: 27195

First of all: Drop the echo part. You can use the variable directly.

Back to the actual problem:
The difference is how you encode the newline. In the first command you wrote \n do denote a newline. That \n is not interpreted by 'bash', but directly sent to sed. In the second command, the file content is sent to sed, including literal newline characters. For a file with the two lines line1 and line2 the command sed sees is

3s/.*/line1
line2/

sed cannot handle such multi-lined commands.

Non-sed solution:
As it seems, you just want to replace the third line of one file with the content of another file. This can be done with

cat <(head -n 2 file1) file2 <(tail -n +4 file1)  

head -n2 file1 prints the first two lines of file1.
tail -n +4 file 1 prints all lines of file1 starting at line 4.
<(command) is called process substitution and emulates a file containing the output of command.
cat concatenates the three "files".

Upvotes: 1

Related Questions