tags:

views:

36

answers:

1

Hello,

I have a file named "insert.txt". It can look like this (or uglier):

ASDFG?|??|?\/\HJKL<MNBVCXZQWERTYUIOP
zxvbnmlkjhgfdsaqwertyuiop
123"'`~4567890987654321!@#$%^&*()
@#$%^&*()+_}{":?>

And I want to replace a block of text in a target file (target.txt) which is delimited by "STARTSTACKOVERFLOW" to "STOPSTACKOVERFLOW". (I am simplifying the question a little here, but it's the same).

The bash script I use to do this is:

TARGETFILE=target.txt
SOURCEFILE=insert.txt
SOURCETXT="$(<$SOURCEFILE)"
DELIMTXT=$(printf "%q" "$SOURCETXT")

sed -i -e "/STARTSTACKOVERFLOW/,/STOPSTACKOVERFLOW/cSTARTSTACKOVERFLOW\n\n${DELIMTXT}\n\nSTOPSTACKOVERFLOW\n" $TARGETFILE

The problem is that what is pasted into "target.txt" is actually ANSI-C quoted:

$'ASDFG?|??|?\/\HJKL<MNBVCXZQWERTYUIOP
zxvbnmlkjhgfdsaqwertyuiop
123"'`~4567890987654321!@#$%^&*()
@#$%^&*()+_}{":?>'

Note the $'' added.

The reason is that the printf "%q" is producing this quoting style. I would like to avoid that - though need it because I must escape all the badness in this file.

Is there a better way of doing the above using bash and sed?

A: 

POSIX sed has an 'r' command to read from a file. So:

sed -i -e '/STARTSTACKOVERFLOW/,/STOPSTACKOVERFLOW/r large.txt' target.txt

The only issue is whether the file is read once, or once per line between the start and stop lines. I suspect it is read once per line...and working out how to throw the extra lines away is harder...but maybe:

sed -i -e '/STOPSTACKOVERFLOW/r large.txt' \
       -e '/STARTSTACKOVERFLOW/,/STOPSTACKOVERFLOW/d' target.txt

Simple demo

This version removes the start and end markers.

$ cat data
sdasas
adsasdas
start
more
more
end
sdasda
sdasdad
$ cat replace
replace1
replace2
replace3
$ sed -e '/^end$/r replace' -e '/start/,/end/d' data
sdasas
adsasdas
replace1
replace2
replace3
sdasda
sdasdad

Preserving the start and end markers

$ cat sedfile
/^end$/{
a\
start
r replace
a\
end
}
/^start$/,/^end$/d
$ sed -f sedfile data
sdasas
adsasdas
start
replace1
replace2
replace3
end
sdasda
sdasdad
$ 

This is fiddlier - I would not try to do it without using a file for the script, but you could do so if you wanted to. It is not a one-liner, though.

Jonathan Leffler
Thanks Jonathan - I appreciate your response, though, indeed your solution inserts the text of insert.txt once per line found between the delimiters. I definitely prefer the option of using the "r" command instead of the "c" but doesn't seem like this will work.
stackmate
@stackmate: try the fixed version - sorry about being distracted. I plan to delete this comment if you agree the solution works.
Jonathan Leffler
Thanks Jonathan - I did want to preserve the delimiters though (in your case, start and end).
stackmate
@stackmate: It's probably easiest to print new delimiter before and after reading in the insert file - make that into a multi-action operation.
Jonathan Leffler