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 r
eads 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