3

I've two files. One is called file1.txt and the other is person1.txt. Both of them contain some lines of text as in the following example:

  • file1.txt
    word1
    word2
    word3
    word4
    word5
    word6
    word7
    word8
    word9
    
  • person1.txt
    givi sixarulidze
    

What I want to do is to replace first seven lines of code in file1.txt with the content of person1.txt.

  • Desired output:
    givi sixarulidze
    word8
    word9
    

How can I achieve that?

2
  • 2
    What have you tried? Have you researched the answers here or done research via Google to find a solution? Commented Nov 6, 2022 at 23:46
  • Is it guaranteed that person1.txt has less then seven lines? What should the preferred solution do if person1.txt is longer? Commented Nov 7, 2022 at 13:40

5 Answers 5

14
cp person1.txt result.txt
tail -n+8 file1 >> result.txt
8

Using GNU sponge to do in-place editing of file1.txt:

{ cat person1.txt; tail -n +8 file1.txt; } | sponge file1.txt

This concatenates the person1.txt file with all but the 7 first lines from the file1.txt file and saves the result into file1.txt.

With sed, you would do the same with

sed -e '1r person1.txt' -e '1,7d' file1.txt | sponge file1.txt

(you could use 8,$!d in place of 1,7d), or, if using GNU sed,

sed -i -e '1r person1.txt' -e '1,7d' file1.txt

This inserts the contents of person1.txt at the start and then skips the first seven lines from the file1.txt input file.

8

Another possibility is awk:

awk 'NR==FNR || FNR>7' person1.txt file1.txt > result.txt

This will process first person1.txt and then file1.txt. It will print the current line if either

  • we are processing the first of the two files, where NR (the global line-counter) is equal to FNR, the per-file line counter, or
  • the per-file line-counter is larger than 7 (which will only be a limitation when processing the second file, where NR is now larger than FNR)

You even can expand this to per-file specifications of lines to be skipped:

awk -- '--skip<=0' person1.txt skip=7 file1.txt skip=4 file2.txt >result.txt

You would then simply precede each file name with a variable assignment that sets the awk variable skip to the number of lines you want to have skipped from the respective input file. The awk program checks for each processed line whether this variable, if decremented by 1, falls below zero, which would indicate that the number of lines to be skipped has been reached. The condition would then be true, and the current line printed.

Notice that the -- "end-of-options" specifier is needed because the first command of the actual program starts with a --.

4
  • 1
    Using awk is my preferred method for this too, although I'd just do ( cat person1.txt; awk 'NR > 7' file1.txt ) > result.txt. Commented Nov 7, 2022 at 17:00
  • @JivanPal That's also rather compact, however, in that case I would recommend { cat person1.txt; awk 'NR>7' file1.txt; } > result.txt instead to avoid the extra sub-shell. Commented Nov 8, 2022 at 8:58
  • @AdminBee what's the benefit of avoiding the extra subshell? Just avoiding the overhead of launching one? Could that be significant on any modern system? Embedded perhaps? Or simply a general principle of efficiency: "don't use more resources than actually needed"? Commented Nov 8, 2022 at 10:25
  • @terdon As you correctly assumed, the resource overhead in most cases doesn't really matter. However, I consider it good practice not getting too cozy with (possibly nested) sub-shells, because this may have you end up with problems where the scope/lifetime of settings is limited to your sub-shell and you wonder why your main script doesn't seem to notice the settings. Commented Nov 8, 2022 at 10:52
7

This can be done with sed

The steps are to delete the first 6 lines. When you get to the 7th you need to read in the other file and then delete the 7th line.

sed '1,6d
7{
r person1.txt
d
} ' file1.txt > newfile1.txt &&
mv newfile1.txt file1.txt

Some versions of sed have a -i flag to do an "in place" edit.

Another way is to use tail.

{
    cat person1.txt
    tail -n +8 file1.txt
} > newfile1.txt && mv -f newfile1.txt file1.txt

Here the two commands are put together and the output redirected together.

1
  • 2
    Or sed -e 7rfile2 -e 1,7d Commented Nov 7, 2022 at 8:09
4
$ ed file1
1,7d
0r person1
w
q

Or, as a script:

ed -s file1 <<'EOF'
1,7d
0r person1
w
q
EOF

or

printf '%s\n' 1,7d '0r person1' w q | ed -s file1

Nowadays, ex (being installed with vi) is more common than ed, although both are specified in POSIX and the latter is simpler. For real-world portability, just replace ed with ex in the code samples.

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.