Hi I want to prepend text to a file. For example I want to add tasks to the beginning of a todo.txt file. I am aware of echo 'task goes here' >> todo.txt but that adds the line to the end of the file (not what I want).
- 3There if no efficient way to do that for large files: unix.stackexchange.com/questions/87772/…Ciro Santilli OurBigBook.com– Ciro Santilli OurBigBook.com2015-11-22 19:45:30 +00:00Commented Nov 22, 2015 at 19:45
- It baffles me how trivial is to append to and end but no trivial solution to do the same at the beginning on linux :(Reuel Ribeiro– Reuel Ribeiro2024-10-14 14:35:21 +00:00Commented Oct 14, 2024 at 14:35
11 Answers
Linux :
echo 'task goes here' | cat - todo.txt > temp && mv temp todo.txt or
sed -i '1s/^/task goes here\n/' todo.txt or
sed -i '1itask goes here' todo.txt Mac os x :
sed -i '.bak' '1s/^/task goes here\'$'\n/g' todo.txt or
echo -e "task goes here\n$(cat todo.txt)" > todo.txt or
echo 'task goes here' | cat - todo.txt > temp && mv temp todo.txt - 11the first one works great! would you mind explaining the logic? im not particularly sure how to interpret the syntax.user479534– user4795342011-02-18 04:24:36 +00:00Commented Feb 18, 2011 at 4:24
- 70@user8347: Pipe (
|) the message (echo '...') tocatwhich uses-(standard input) as the first file andtodo.txtas the second.catconCATenates multiple files. Send the output (>) to a file namedtemp. If there are no errors (&&) fromcatthen rename (mv) thetempfile back to the original file (todo.txt).Dennis Williamson– Dennis Williamson2011-02-18 04:51:26 +00:00Commented Feb 18, 2011 at 4:51 - 1@itaifrenkel: I'd have to see what you did, but if
catreceives a literal backslash n, it won't convert it to a newline. Something else must have done that. Instead ofcat, try piping intohexdump -Cto see if you're actually sending backslash and n or if it's a newline. You could also trycat -eto show line endings.Dennis Williamson– Dennis Williamson2014-01-26 21:58:05 +00:00Commented Jan 26, 2014 at 21:58 - 2Using 2 and 3 (3 seems simpler to me) allows you to prepend text to many files at once.Felix– Felix2014-01-27 15:24:15 +00:00Commented Jan 27, 2014 at 15:24
- 6@Kira: The
1means do the next command only on line one of the file and theicommand is insert. Look in the man page under the "Addresses" section and in the "Zero- or One- address commands" section.Dennis Williamson– Dennis Williamson2015-10-23 16:33:52 +00:00Commented Oct 23, 2015 at 16:33
A simpler option in my opinion is :
echo -e "task goes here\n$(cat todo.txt)" > todo.txt This works because the command inside of $(...) is executed before todo.txt is overwritten with > todo.txt
While the other answers work fine, I find this much easier to remember because I use echo and cat every day.
EDIT: This solution is a very bad idea if there are any backslashes in todo.txt, because thanks to the -e flag echo will interpret them. Another, far easier way to get newlines into the preface string is...
echo "task goes here $(cat todo.txt)" > todo.txt ...simply to use newlines. Sure, it isn't a one-liner anymore, but realistically it wasn't a one-liner before, either. If you're doing this inside a script, and are worried about indenting (e.g. you're executing this inside a function) there are a few workarounds to make this still fit nicely, including but not limited to:
echo 'task goes here'$'\n'"$(cat todo.txt)" > todo.txt Also, if you care about whether a newline gets added to the end of todo.txt, don't use these. Well, except the second-to-last one. That doesn't mess with the end.
- 2That might work better (or at all) with double quotes instead of single…raphink– raphink2013-05-22 07:25:01 +00:00Commented May 22, 2013 at 7:25
- 6Won't the
-ealso convert escape sequences inside todo.txt?mk12– mk122013-09-02 21:23:29 +00:00Commented Sep 2, 2013 at 21:23 - 2Workarounds yield --> cat: <destination filename>: input file is output file ...ingyhere– ingyhere2015-01-09 02:10:05 +00:00Commented Jan 9, 2015 at 2:10
- 2I'm not sure this works with large files, bash string size is probably limited to a few MB.jesjimher– jesjimher2018-05-10 11:06:43 +00:00Commented May 10, 2018 at 11:06
- 2a little warning here: it can be dangerous to read and directly overwrite a file this way... for one thing, what if the commands happen to operate like this: read position 10000000 to 10100000 and write to the file, but now that position 10000000 is already of the new file? Even if that won't happen, what if the file system becomes full during this, so the new file is being written, but disk full, and fail, and now where is that old file for you to redo this operation when you clean up some disk space? You may lose this file or (part of it)nonopolarity– nonopolarity2022-12-29 12:27:12 +00:00Commented Dec 29, 2022 at 12:27
The moreutils have a nice tool called sponge:
echo "task goes here" | cat - todo.txt | sponge todo.txt It'll "soak up" STDIN and then write to the file, which means you don't have to worry about temporary files and moving them around.
You can get moreutils with many Linux distros, through apt-get install moreutils, or on OS X using Homebrew, with brew install moreutils.
- 1I would go for
(echo 'ble'; cat todo.txt):-)Ciro Santilli OurBigBook.com– Ciro Santilli OurBigBook.com2018-12-04 09:37:35 +00:00Commented Dec 4, 2018 at 9:37 - I would use
teeinstead of sponge which come by default on most distroecho "task goes here" | cat - todo.txt | tee todo.txtAngel115– Angel1152020-04-28 17:12:47 +00:00Commented Apr 28, 2020 at 17:12 - 1The
teeexample is not correct askubuntu.com/a/752451Zombo– Zombo2020-05-01 14:51:01 +00:00Commented May 1, 2020 at 14:51 - @stevenpenny Good point. Now I remember why I didn't suggest it originally. I came back to this answer not noticing that the output file name was the same as the input, which is why you need sponge to buffer it all before writing it back out.slhck– slhck2020-05-02 12:07:39 +00:00Commented May 2, 2020 at 12:07
You can use the POSIX tool ex:
ex a.txt <<eof 1 insert Sunday . xit eof https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ex.html
You can create a new, temporary file.
echo "new task" > new_todo.txt cat todo.txt >> new_todo.txt rm todo.txt mv new_todo.txt todo.txt You might also use sed or awk. But basically the same thing happens.
- 3Say you're out of disk space so that
new_todo.txtgets written only partially. Your solution appears to lose the original file.NPE– NPE2011-02-17 10:31:29 +00:00Commented Feb 17, 2011 at 10:31 - 1Who runs out of disk space? ;-) It's just a simple example.Keith– Keith2011-02-17 10:33:56 +00:00Commented Feb 17, 2011 at 10:33
- 3@Keith Someone working on a VM who didn't expect to need a particularly large virtual drive. Or someone moving a large file. In any case, the real argument against this is directory permissions; if you don't have permission to create new files in the given directory, the only command that will successfully execute in your script is the
rmof the original file.Parthian Shot– Parthian Shot2014-07-01 20:23:30 +00:00Commented Jul 1, 2014 at 20:23
If the text file is small enough to fit in memory, you don't have to create a temporary file to replace it with. You can load it all into memory and write it back out to the file.
echo "$(echo 'task goes here' | cat - todo.txt)" > todo.txt It's impossible to add lines to the beginning of the file without over writing the whole file.
- Just to ask the obvious question: Where's the character limit of shell variables?nixda– nixda2013-01-09 22:59:50 +00:00Commented Jan 9, 2013 at 22:59
- As far as I'm aware, it's only limited by the amount of memory available. I've filled up variables well over 100MB into memory.
text=$(cat file). Be careful to only use text though, because shell variables aren't binary clean mywiki.wooledge.org/BashFAQ/058Rucent88– Rucent882013-01-14 01:48:04 +00:00Commented Jan 14, 2013 at 1:48
You cannot insert content at the beginning of a file. The only thing you can do is either replace existing content or append bytes after the current end of file.
Any solution to your question then requires a temporary file (or buffer) to be created (on memory or on disk) which will eventually overwrite the original file.
Beware of not losing data by preserving the original file while creating the new one, should the file system happen to be full during the process. eg:
cat <(echo task go there) todo.txt > todo.txt.new && mv todo.txt.new todo.txt - 1Downvoters are welcome to explain their motivation. None of the remaining answers, including the accepted one, do contradict anything in my reply.jlliagre– jlliagre2016-04-12 13:13:53 +00:00Commented Apr 12, 2016 at 13:13
- This is difficult to parse as the < ... > look like brackets, which I assume they are not. A space between the < and the ( might help?dumbledad– dumbledad2018-06-01 09:20:36 +00:00Commented Jun 1, 2018 at 9:20
- This is not working for me.
echo HOME=\"/g/Users/timregan/\" | cat - 'F:\Program Files\Git\etc\profile'works butcat <echo HOME=\"/g/Users/timregan/\" 'F:\Program Files\Git\etc\profile'gives the error "echo: No such file or directory"dumbledad– dumbledad2018-06-01 09:35:16 +00:00Commented Jun 1, 2018 at 9:35 - @dumbledad You are overthinking my reply. There is nothing for you to parse in it. A space between the
<and the(would break the syntax. Trycat <(echo HOME=\"/g/Users/timregan/\") 'F:\Program Files\Git\etc\profile'jlliagre– jlliagre2018-06-02 01:11:04 +00:00Commented Jun 2, 2018 at 1:11
Final answer
cat <<< "prepended text $(cat test.txt)" > test.txt Context
I wasn't too satisfied with the answers as they felt like too much typing. I liked John Alberts his answer but couldn't stand to type -e. Unfortunately, I accidentally read over John Alberts his echo 2 liner as well (significantly reducing the value of this answer and me 30 minutes playing around, but oh well, it happens).
In any case, I was focused on finding something that meant you only needed to type the filename and text you want to prepend.
Moreover, I was searching for something that looked aesthetically intuitive. With that I mean: the preprend needs to physically show, even if it'd be an illusion it'd have the effect of a mnemonic.
So I tried an approach with herestrings since in the right context they reduce cognitive strain (i.e. typing < 3 times doesn't require too much thinking power).
I created a file test.txt with the word "monkeys".
And I typed:
cat <<< "prepend > $(< test.txt)" Output:
prepend monkeys A bit of clarification:
You need to manually press enter yourself.
On the second line the > is from the shell itself, you don't need to type that.
Notes:
(1) What I couldn't manage was a one liner. There seems to be no herestring combination in which I could use $() and \n. Which is why you need to press the newline manually yourself.
(2) $(< test.txt) has the same effect as $(cat test.txt). The Bash Reference Manual states:
The command substitution
$(cat file)can be replaced by the equivalent but faster$(< file).
So you could also do:
cat <<< "prepend > $(cat test.txt)" More typing, but I admit a bit less cognitive strain since cat is being typed twice and is more well-known than the trick of the Bash Reference Manual.
You can use tee:
echo 'task goes here' | cat - todo.txt | tee todo.txt - 7No you cannot askubuntu.com/a/752451Zombo– Zombo2016-04-11 00:25:19 +00:00Commented Apr 11, 2016 at 0:25
GitBash + Windows10 + Multline:
Here is a version that lets you use multi-line strings.
############################################## ## This section for demo purpose only, ## ## So you can save entire file as ## ## whatever.sh and run it. ## ## ## ############################################## > MY_TARGET_FILE.txt ##Make Or Clear File echo "[STARTER_CONTENT]" >> MY_TARGET_FILE.txt ############################################## ## Below is main code: ################################################## TARGET_FILE_VARIABLE="MY_TARGET_FILE.txt" ADD_TO_HEAD_VARIABLE=$(cat << "HEREDOC_HEAD_TEXT" //| +-------------------------------------+ |// //| | | |// //| | MESSAGE_FOR_HEAD_OF_FILE | |// //| | | |// //| +-------------------------------------+ |// HEREDOC_HEAD_TEXT ) ################################################## TAR=$TARGET_FILE_VARIABLE ## TEX=$ADD_TO_HEAD_VARIABLE ## echo "$TEX" | cat - $TAR > TEMP && mv TEMP $TAR ## ################################################## ## Expected contents of MY_TARGET_FILE.txt : ## //| +-------------------------------------+ |// ## //| | | |// ## //| | MESSAGE_FOR_HEAD_OF_FILE | |// ## //| | | |// ## //| +-------------------------------------+ |// ## [STARTER_CONTENT] Maybe this, in two commands, is easier to understand. Say that we want to prepend to hello.c from hello.h.
$ cat hello.c >> hello.h $ mv hello.h hello.c Before:
% cat hello.c int main() { printf("Hello, World!"); return 0; } % cat hello.h #include <stdio.h After:
% cat hello.c #include <stdio.h> int main() { printf("Hello, World!"); return 0; }