Replace a word with multiple lines using sed?

2019-01-06 20:59发布

问题:

I'm working on a bash-script that has to prepare an E-Mail for being sent to a user.

It aggregates some data, which ends up being multiple lines of stuff. For the example stored in $DATA.

Now, after a bit of stfw I found a few things like sed -ei "s/_data_/${DATA}/g" mail.tpl and also sed replace with variable with multiple lines. None of them work.

Now the question is, how do I get sed to replace something with multiple lines of text?

(Alternatives to sed are also welcome!)

回答1:

If you build your multiple line text with "\n"s, this will work with a simple sed command as:

DATA=`echo ${DATA} | tr '\n' "\\n"`
#now, DATA="line1\nline2\nline3"
sed "s/_data_/${DATA}/" mail.tpl


回答2:

awk?

kent$  cat test
foo
bar
blah _data_and_data_
foo
_data_ foobar _data_ again


kent$  echo $DATA
1
2
3
4
5

kent$  awk -v r=$DATA '{gsub(/_data_/,r)}1' test
foo
bar
blah 1
2
3
4
5and1
2
3
4
5
foo
1
2
3
4
5 foobar 1
2
3
4
5 again


回答3:

I would suggest simply replacing sed with perl command like this:

perl -i.bak -pe 's/_data_/'"${DATA}"'/g' mail.tpl 


回答4:

I tried it and sed 's/pattern/\na\nb\nc/g' but it does not work on all systems. What does work is putting a \ followed by a newline in the replace pattern, like this:

sed 's/pattern/a\
b\
c/g'

This appends a line containing b and a line containing c when the pattern is seen.

To put it in a variable, use double backslashes:

export DATA="\\
a\\
b\\
c"

and then:

sed "s/pattern/${DATA}/g"

Note the double quotes.



回答5:

ring bearer's answer didn't work for me; I think the usage of tr there is wrong, and the way it's written, it simply strips away newlines by use of echo.

Instead, I used sed. I used code from another answer to replace newlines (credit: Zsolt Botykai). I also expected some dollar signs ($) in my input so I took care of that too. You might need to add other input handling. Note the use of double quotes in echo to preserve newlines.

DATA="$(cat whatever)"
ESCAPED_DATA="$(echo "${DATA}" | sed ':a;N;$!ba;s/\n/\\n/g' | sed 's/\$/\\$/g')"

Then you can use ${ESCAPED_DATA} in sed:

cat input | sed 's/one liner/'"${ESCAPED_DATA}"'/' > output 

Just thought I'd share.



回答6:

Escaping all the newlines with a \ (except the last one) worked for me. The last newline must not be escaped not to break the s command.

Example :

DATA="a
b
c"

ESCAPED=$(echo "${DATA}" | sed '$!s@$@\\@g')
echo "${ESCAPED}" 
a\
b\
c

sed "s/pattern/${ESCAPED}/" file


回答7:

You can put your data in a temp file and run:

$ sed '/_data_/r DATA_FILE' mail.tpl | sed '/_data_/d'> temp; mv temp mail.tpl


回答8:

Echo variable into temporary text file.

Insert text file into mail.tpl and delete data from mail.tpl

echo ${DATA} > temp.txt    
sed -i -e "/_data_/r temp.txt" -e "//d" mail.tpl


回答9:

Not sure if you have tried to put "\n" in the replace part

sed 's/[pattern]/\
[line 1]\n\
[line 2]\n\
[line n]\n\
/g' mail.tpl

The first line has /\ for readibility reasons. Each line after that is a stand-alone line like you would find in a text editor. Last line is stand-alone, once again for readability reasons. You can make all of this one line if needed. Works on Debian Jessie when I tested it.



标签: bash replace sed