34

Say I have a text file like this:

# custom content section
a
b

### BEGIN GENERATED CONTENT
c
d
### END GENERATED CONTENT

I'd like to replace the portion between the GENERATED CONTENT tags with the contents of another file.

What's the simplest way to do this?

smokris
  • 1,572
  • 6
  • 21
  • 32

4 Answers4

44
lead='^### BEGIN GENERATED CONTENT$'
tail='^### END GENERATED CONTENT$'
sed -e "/$lead/,/$tail/{ /$lead/{p; r insert_file
        }; /$tail/p; d }"  existing_file
Peter.O
  • 3,023
  • 1
  • 28
  • 30
  • 1
    Excellent. `sed` can do so much more than just `s/.../...`! – DevSolar Jun 22 '12 at 08:28
  • Hmmm does not work for me, should I put the sed command on one line? – lzap Jul 11 '14 at 11:15
  • 3
    The `r insert_file` command must be the last thing on its line. *Note* that neither whitespace nor comments are allowed after it. The code was tested using GNU `sed` with the `--posix` option enabled, and it worked as expected, so it should work with any *posix compliant* `sed`. – Peter.O Jul 12 '14 at 01:58
  • 1
    Holy moly, that's cool! This is going in my sed dictionary! Superbly useful and beautifully simple. Thank you! – DanielSmedegaardBuus Jan 14 '15 at 17:39
  • 2
    Note an in-place editing requires you capture the output of sed (e.g. `output=$(sed -e "..." existing_file)`) and then perform the replacement in a second pass (e.g. `echo "$output" > existing_file`) since trying to redirect into a file you're reading from will cause it to truncate before contents are read. – Chris Tonkinson Aug 02 '16 at 12:44
  • 3
    can someone explain the command. – Prabhat Kumar Singh Feb 19 '19 at 10:52
  • Note that it can fit on a single line by using two `-e "..."` expressions: `sed -e "[...] r insert_file" -e "}; [...]"`. The filename must be followed by the closing `"` without any space. – calandoa May 09 '23 at 17:43
7
newContent=`cat new_file`
perl -0777 -i -pe "s/(### BEGIN GENERATED CONTENT\\n).*(\\n### END GENERATED CONTENT)/\$1$newContent\$2/s" existing_file
smokris
  • 1,572
  • 6
  • 21
  • 32
5

Using a heredoc and the ed line editor:

ed -s FILE1 <<EOF
/### BEGIN GENERATED/+,/### END GENERATED/-d
/### BEGIN GENERATED/ r FILE2
w
q
EOF

The first line inside the heredoc is to delete (d) the line after (+) the ### BEGIN GENERATED and the line before (-) the ### END GENERATED...

The second line is to insert FILE2 after the line ### BEGIN GENERATED.

Destroy666
  • 5,299
  • 7
  • 16
  • 35
Jetchisel
  • 51
  • 1
  • 2
  • Using a heredoc and the ed line editor. The first line inside the heredoc is to deleted 'd' the line after '+' the '### BEGIN gENERATED...' and the line before '-' the '### END GENERATED...' the second line is to insert FILE2 after the line ### END GENERATED...' – Jetchisel Sep 03 '14 at 23:27
  • sorry what i meant was insert FILE2 after the line '### BEGIN GENERATED..' – Jetchisel Sep 03 '14 at 23:47
  • Take it easy on me folks, it is after all my first time :-). Also i might have used the word the same but the other solution does not use a heredocs but a printf and a pipe. Any way apologies if i have made a mistake :-) – Jetchisel Sep 08 '14 at 00:41
  • 1
    Thanks, very readable! Btw - instead of `r FILE2` you can say `r !command` to read from a different script. – Kos Sep 18 '19 at 10:25
4

Warning: This is definitely not the simplest way to do it. (EDIT: bash works; POSIX grep is fine too)

If the main text is in file "main" and the generated content is in file "gen", you could do the following:

#!/bin/bash
BEGIN_GEN=$(cat main | grep -n '### BEGIN GENERATED CONTENT' | sed 's/\(.*\):.*/\1/g')
END_GEN=$(cat main | grep -n '### END GENERATED CONTENT' | sed 's/\(.*\):.*/\1/g')
cat <(head -n $(expr $BEGIN_GEN - 1) main) gen <(tail -n +$(expr $END_GEN + 1) main) >temp
mv temp main
Dr Kitty
  • 534
  • 1
  • 5
  • 12