Home > Net >  how to replace a block of text from a file with a block of text from another file using shell (bash)
how to replace a block of text from a file with a block of text from another file using shell (bash)

Time:01-19

I'm trying to replace a block of text from file_to_change.xml with the entire content of changes.xml, which is a tag of the main file with a minor change.

This is the content of file_to_change.xml:

<clients>
    <client>
        <name>Bob</name>
        <age>18</age>
    </client>
    <client>
        <name>Alice</name>
        <age>12</age>
    </client>
    <client>
        <name>Carlos</name>
        <age>28</age>
    </client>
</clients>

And this is the content of changes.xml:

    <client>
        <name>Carlita</name>
        <age>17</age>
    </client>

And this is the content of an auxiliary file that contains the line i want to change:

    <client>
        <name>Carlos</name>
        <age>28</age>
    </client>

The great difference between the solution i'm looking for from the one explained here is that the files are not entirely equal. It is also important to say that the environment i'm working at do not allow me to use external tools, and for that reason i'm trying to find a "core" solution, using sed, awk, etc.

This is the command i was trying to use:

sed -i -e '1r changes.xml' -e '1,/r aux.xml/d' file_to_change.xml

I'm expecting that the file_to_change.xml look like this:

<clients>
    <client>
        <name>Bob</name>
        <age>18</age>
    </client>
    <client>
        <name>Alice</name>
        <age>12</age>
    </client>
    <client>
        <name>Carlita</name>
        <age>17</age>
    </client>
</clients>

CodePudding user response:

Using pure bash?

#!/bin/bash

file=$( < file_to_change.xml )
search=$( < aux.xml )
replacement=$( < changes.xml )

printf '%s\n' "${file/"$search"/$replacement}"
<clients>
    <client>
        <name>Bob</name>
        <age>18</age>
    </client>
    <client>
        <name>Alice</name>
        <age>12</age>
    </client>
    <client>
        <name>Carlita</name>
        <age>17</age>
    </client>
</clients>

CodePudding user response:

I would harness GNU AWK for this task following way, let file.txt content be

ABC
DEF
GHI
JKL
MNO

and file_detect.txt be

GHI
JKL

and file_replacement.txt be

123
456
789

then

awk 'BEGIN{RS=""}FILENAME=="file_replacement.txt"{repl=$0;next}FILENAME=="file_detect.txt"{find=$0;next}{place=index($0,find);print substr($0,1,place-1) repl substr($0,place length(find))}' file_replacement.txt file_detect.txt file.txt

gives output

ABC
DEF
123
456
789
MNO

Disclaimer: this solution assumes you have never blank lines in any of your files, if this does not hold change RS to any value which does not exist in any of files involved.

Explanation: firstly I inform GNU AWK that blank lines are to be considered row separators using RS set to empty string, aim there is to make GNU AWK consider whole file as big single line. Then when I encounter file named file_replacement.txt I simply store its' content into variable named repl and instruct GNU AWK to go to next line, so no other action is undertaken, similarly content of file_detect.txt are stored in variable named find. Then I use String functions following way, first index to detect where part to be replace is located, then I use substr to get part before part to be replaced substr($0,1,place-1) and part after part to be replaced substr($0,place length(find) and then print them concatenated using repl.

Warning: this solution assumes there is exactly one file_detect.txt inside file.txt.

(tested in GNU Awk 5.0.1)

  • Related