user301200
user301200

Reputation: 201

How can I can insert the contents of a file into another file right before a specific line

How can I can insert the contents of a file into another file right before a specific line using sed?

example I have file1.xml that has the following:

        <field tagRef="376">
        </field>
        <field tagRef="377">
        </field>
        <field tagRef="58">
        </field>
        <group ref="StandardMessageTrailer" required="true"/>
    </fieldList>
</message>

and file2.xml has the following:

        <field tagRef="9647">
            <description>Offset</description>
        </field>
        <field tagRef="9648">
            <description>Offset Units/Direction</description>
        </field>
        <field tagRef="9646">
            <description>Anchor Price</description>
        </field>

how can I insert the contents of file2 into file1 just before

<group ref="StandardMessageTrailer" required="true"/>

so it will look like this:

       <field tagRef="376">
        </field>
        <field tagRef="377">
        </field>
        <field tagRef="58">
        </field>
        <field tagRef="9647">
            <description>Offset</description>
        </field>
        <field tagRef="9648">
            <description>Offset Units/Direction</description>
        </field>
        <field tagRef="9646">
            <description>Anchor Price</description>
        </field>
        <group ref="StandardMessageTrailer" required="true"/>
    </fieldList>
</message>

I know how to insert after that line using

sed 'group ref="StandardMessageTrailer"/r file2.xml' file1.xml > newfile.xml  

but I want to insert it before.

appreciate the help

Upvotes: 20

Views: 26186

Answers (8)

ufopilot
ufopilot

Reputation: 3975

awk

awk '
    FNR==NR{
      a[NR] = $0
      next
     } 
    /StandardMessageTrailer/{
      for(i=1;i<=length(a);i++) print a[i]
    }1
' file2.xml file1.xml

        <field tagRef="376">
        </field>
        <field tagRef="377">
        </field>
        <field tagRef="58">
        </field>
        <field tagRef="9647">
            <description>Offset</description>
        </field>
        <field tagRef="9648">
            <description>Offset Units/Direction</description>
        </field>
        <field tagRef="9646">
            <description>Anchor Price</description>
        </field>
        <group ref="StandardMessageTrailer" required="true"/>
    </fieldList>
</message>

Upvotes: 0

RARE Kpop Manifesto
RARE Kpop Manifesto

Reputation: 2815

Assuming these are the inputs file1 and file2...

---------
0x423A417F
0x42BED4B9
0x434367F3
---------

---------
abcd
xyzw
        <group ref="StandardMessageTrailer" required="true"/>
    </fieldList>
</message>
---------

…this approach loads the full file1 in 1 shot, then revert back to checking individual rows for file2, and simply prepends it when the search phrase is found

mawk 'FNR == NR ? (__ = $_) * (RS = ORS)  \
                : !/StandardMessageTrailer/ || $!NF = __$_' RS='^$' 
 <( jot -w '0x%.8X' 3  1111114111 - 8688442 ) 
 <( echo "abcd\nxyzw" |
    mawk 'END {
        print "        <group ref=\"StandardMessageTrailer\""\
              " required=\"true\"/>\n    </fieldList>\n</message>" } 1' ) 
abcd
xyzw
0x423A417F
0x42BED4B9
0x434367F3
        <group ref="StandardMessageTrailer" required="true"/>
    </fieldList>
</message>

By using RS = "^$" instead of RS = "", blank rows in file1 will be fully preserved.

Upvotes: 0

Daniel Alder
Daniel Alder

Reputation: 5372

personally, I prefer this solution. It is a bit slower than a single call, but easier to read for a programmer:

awk '/StandardMessageTrailer/{exit}{print}' file1.xml >output.xml  # lines until token
cat file2.xml >>output.xml  # the include file
awk '/StandardMessageTrailer/{o=1} o==1{print}' file1.xml >>output.xml  # lines from (and including) token

or the last command, if the token should not be included in the output:

awk 'o==1{print} /StandardMessageTrailer/{o=1}' file1.xml >>output.xml  # lines after token

Upvotes: 0

Anthony O.
Anthony O.

Reputation: 24297

Here were the solutions that worked for me:

  1. Using a marker like explained in another reply:

    sed '/StandardMessageTrailer/i MARKER' file1.xml | sed -e '/MARKER/r file2.xml' -e '/MARKER/d'
    
  2. Counting the line when it occured like explained in another reply to a similar question:

    LINE_NUMBER_MATCHING=$(sed -n '/StandardMessageTrailer/=' file1.xml) && sed "$((${LINE_NUMBER_MATCHING} - 1))r file2.xml" file1.xml
    
  3. Or using sed like explained in another reply to a similar question:

    sed $'/StandardMessageTrailer/{e cat file2.xml\n}' file1.xml
    

Upvotes: 1

Thibault Deheurles
Thibault Deheurles

Reputation: 1269

I tried the different solutions and the one from Beta did the work for me.

Summary :

  • I wanted to insert different files into a main file
  • I wanted to use markers to say where I wanted these files to be inserted

Example :
Create 2 files :

cloud_config.yml:

coreos:
__ETCD2__

etcd2.yml :

  etcd2:
    name:                         __HOSTNAME__
    listen-peer-urls:             http://__IP_PUBLIC__:2380
    listen-client-urls:           http://__IP_PUBLIC__:2379,http://127.0.0.1:2379

Then we run that script on it :

sed '/Standard/i __ETCD2__' cloud_config.yml \
| sed -e "/__ETCD2__/r etcd2.yml" > tmpfile
sed "s|__ETCD2__||g" tmpfile > cloud_config.yml

Finally, we got that :

coreos:
  etcd2:
    name:                         __HOSTNAME__
    listen-peer-urls:             http://__IP_PUBLIC__:2380
    listen-client-urls:           http://__IP_PUBLIC__:2379,http://127.0.0.1:2379

Upvotes: 0

Tong
Tong

Reputation: 2127

Usually I do like this:

  1. file1, file to read insert content
  2. file2, insert reading content from file1 to at the head of file2
  3. script snippet:

    sed "\$r ${file2}" ${file1} > tmpfile
    mv tmpfile ${file2}

Upvotes: 1

Beta
Beta

Reputation: 99094

If you can bear to make two passes, you can use a marker:

sed '/Standard/i MARKER' file1.xml | sed -e '/MARKER/r file2.xml' -e '/MARKER/d'

The trouble with trying to do it in one pass is that there's no way (that I know of) other than 'r' to insert the contents of a file, and 'r' does so in the output stream, out of reach of manipulation, after sed is finished with the line. So if the 'Standard' is in the last line, whatever you do with it will be over by the time file2 appears.

Upvotes: 4

ghostdog74
ghostdog74

Reputation: 342303

f2="$(<file2)"
awk -vf2="$f2" '/StandardMessageTrailer/{print f2;print;next}1' file1 

if you want sed, here's one way

sed  -e '/StandardMessageTrailer/r file2' -e 'x;$G' file1

Upvotes: 23

Related Questions