How to insert lines with numbers from X to Y into another file after line Z?

I created two files:

echo -e "1n2n3n4n5" > 123.txt
echo -e "anbncndne" > abc.txt

I want to get the file 123.txt with the following contents:

1
2
3
b
c
d
4
5

So in other words insert the rows with numbers from 2 to 4 of the file abc.txt into the file 123.txt after the third line.

I have reviewed the many similar questions here, but did not find a suitable answer. Nevertheless I got the lines:

sed -n '2,4p' abc.txt

and put some text into the file after the third line:

sed -i '3amytext' 123.txt

How to do this using the previous command stdout or/and a sed/awk single command?

Asked By: Apostle

||

If your system has the GNU version of sed, you can use the GNU extension r command:

r filename
    As a GNU extension, this command accepts two addresses.

    Queue the contents of filename to be read and inserted into the
    output stream at the end of the current cycle, or when the next input 
    line is read. Note that if filename cannot be read, it is treated as 
    if it were an empty file, without any error indication.

    As a GNU sed extension, the special value /dev/stdin is supported for 
    the file name, which reads the contents of the standard input. 

For example,

$ sed '3r /dev/stdin' 123.txt < <(sed -n '2,4p' abc.txt)
1
2
3
b
c
d
4
5
Answered By: steeldriver

By using awk and sed:

$ awk 'FNR==3{print;system("sed -n '2,4p' abc.txt");next};1' 123.txt 
1
2
3
b
c
d
4
5
Answered By: cuonglm

In-place editing usinged and sed (on the bash prompt):

ed -s 123.txt<<<$'3r !sed -n "2,4p" abc.txtnw'
Answered By: iruvar

With sed, you can use the r command to insert a whole file. To insert a part of a file, extract that part and pipe it as input to sed. You can use sed to do the extraction as well.

sed -n '2,4p' abc.txt | sed -i '3r /dev/stdin' 123.txt

With awk, you can read one file and switch to another file in the middle.

awk <123.txt >123.txt.new '
    1                                    # print the input line
    NR==3 {                              # after line 3 of 123.txt, …
        while (getline < "abc.txt") {
            ++inner_NR;
            if (inner_NR >= 2) print;    # start printing at line 2 from abc.txt
            if (inner_NR == 4) break;    # stop after line 4
        }
    }
' &&
mv 123.txt.new 123.txt

You can also use head and tail to extract the parts of the files that you want to combine.

head -n 3 <123.txt >123.txt.new &&
<abc.txt tail -n +2 | head -n 3 >>123.txt.new &&
tail -n +4 123.txt >>123.txt.new &&
mv 123.txt.new 123.txt

You can combine the head+tail approach with sed. For huge files, this is likely to be the fastest.

<abc.txt tail -n +2 | head -n 3 | sed -i '3r /dev/stdin' 123.txt

Note that all these approaches write to a temporary file which is moved to 123.txt (even the approaches using sed -i, because that’s what sed -i does under the hood).

Categories: Answers Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.