Laurent C.
Laurent C.

Reputation: 206

Add file content in another file after first match only

Using bash, I have this line of code that adds the content of a temp file into another file, after a specific match:

sed -i "/text_to_match/r ${tmpFile}" ${fileName}

I would like it to add the temp file content only after the FIRST match.

I tried using addresses:

sed -i "0,/text_to_match//text_to_match/r ${tmpFile}" ${fileName}

But it doesn't work, saying that "/" is an unknown command.

I can make addresses work if I use a standard replacement "s/to_replace/with_this/", but I can't make it work with this sed command. It seems like I can't use addresses if my sed command starts with / instead of a letter.

I'm not stuck with addresses, as long as I can insert the temp file content into another file only once.

Upvotes: 1

Views: 340

Answers (4)

glenn jackman
glenn jackman

Reputation: 246744

You're getting that error because if you have an address range (ADDR1,ADDR2) you can't put another address after it: sed expects a command there and / is not a command.

You'll want to use some braces here:

$ seq 20 > file
$ echo "new content" > tmpFile
$ sed '0,/5/{/5/ r tmpFile
}' file

outputs the new text only after the first line with '5'

1
2
3
4
5
new content
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

I found I needed to put a newline after the filename. I was getting this error otherwise

sed: -e expression #1, char 0: unmatched `{'

It appears that sed takes the whole rest of the line as the filename.

Probably more tidy to write

sed '0,/5/ {
    /5/ r tmpFile 
}' file

Full transparency: I don't use sed except for very simple tasks. In reality I would use awk for this job

awk '
    {print} 
    !seen && $0 ~ patt {
        while (getline line < f) print line
        close(f)
        seen = 1
    }
'  patt="5" f=tmpFile file

Upvotes: 3

stevesliva
stevesliva

Reputation: 5655

GNU sed also allows s///e to shell out. So there's this one-liner using Glenn's tmpFile and file.

sed '0,/5/{//{p;s/.*/cat tmpFile/e}}' file

  • // to repeat the previous pattern match (helps if it's longer than /5/)
  • p to print the matching line
  • s/.*/cat tmpFile/e to empty the pattern buffer and stick a the cat tmpFile shell command in there and e execute it and dump the output in the stream

Upvotes: 1

kvantour
kvantour

Reputation: 26471

Glenn Jackman provided with an excellent answer to why the OP's attempt did not work.

In continuation to Glenn Jackman's answer, if you want to have the command on a single line, you should use branching so that the r command is at the end.

Editing commands other than {...}, a, b, c, i, r, t, w, :, and # can be followed by a <semicolon>, optional <blank> characters, and another editing command. However, when an s editing command is used with the w flag, following it with another command in this manner produces undefined results.
[source: POSIX sed Standard]

The r,R,w,W commands parse the filename until end of the line. If whitespace, comments or semicolons are found, they will be included in the filename, leading to unexpected results.
[source: GNU sed manual]

which gives:

sed -e '1,/pattern/{/pattern/ba};b;:a;r rfile' file

Upvotes: 2

jasonmclose
jasonmclose

Reputation: 1695

You have 2 forward slashes together, right next to each other in the second sed example.

Upvotes: -1

Related Questions