How to prepend multiple lines to a file

The safest way to do this would be to copy the original file to a temporary location, and the concatenate the new contents with that file into the old name:

tmpfile=$(mktemp)

cp myfile "$tmpfile" &&
cat - "$tmpfile" <<NEW_CONTENTS >myfile
this is
new contents
at top of file
NEW_CONTENTS

rm "$tmpfile"

Notice that the cat command itself is almost the same as your

cat myfile <<EOT >>myfile
1
2
3
EOT

However, cat has to be told to also read its standard input, which it does when one of its filename operands is -. We also can't redirect from the original file and then append the result to the same file, as that creates a loop that would make the file grow until it fills all space on the partition. This is why I first copy the file to a temporary file and then use that to create new contents for the old name.

We could also have done

tmpfile=$(mktemp)

cat - myfile <<NEW_CONTENTS >"$tmpfile" &&
this is
new contents
at top of file
NEW_CONTENTS
mv "$tmpfile" myfile

I.e., write the result to a temporary file and then move that so that it replaces the original. This order of operations is however not guaranteed to preserve ownerships and permissions on myfile.


A small shell function that takes standard input and places it atop a given filename:

paste_header () {
    local tmpfile=$(mktemp)

    trap "rm -f '$tmpfile'" EXIT

    cat - "$1" >"$tmpfile" &&
    cat "$tmpfile" >"$1"
}

The way this is written, it would allow the user to, for example, paste in contents from the clipboard interactively, but would not modify the original file if the input was interrupted by Ctrl+C. The interactive input would need to be terminated by pressing Ctrl+D (which sends EOT, i.e. end-of-text).

The function would be used as

paste_header filename

to interactively paste data to be added into filename (end input with Ctrl+D).

or

paste_header filename <otherfile

to insert data from another file.


If you need to read in the output of a command, you could use ed as in the linked question, with this variation:

ed -s test.txt <<< $'0r !echo stuff\nw\nq'

This reads the output of the command echo stuff into test.txt after line zero.


To insert multi-line text before the 1st line via here-doc you'd run

ed -s test.txt <<EOT
1i
add some line
and some more
to the beginning
.
w
q
EOT

The dot signals the end of input-mode which means the last solution assumes your text doesn't contain lines consisting of single dots.


You can use the plain ol' ex text editor which should be available in almost all POSIX comliant systems.

To insert in the first line you just do

ex -sc '1i|new first line' -cx test.txt

For the case of multiple lines to be added, just add the lines in a loop, with the top most element to be added at the last (like a stack)

for number in 5 4 3 2 1; do
    ex -sc '1i|'"$number"'' -cx newfile
done

of if you want to avoid multiple file write operations on the file, construct a variable with the multi-line content and insert it directly

multiple_lines=$'line1\nline2\nline3\nline4\nline5\n'
ex -sc '1i|'"$multiple_lines"'' -cx newfile