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).
-
2There if no efficient way to do that for large files: unix.stackexchange.com/questions/87772/… – Ciro Santilli 新疆改造中心法轮功六四事件 Nov 22 '15 at 19:45
-
@ould you take a look at this method superuser.com/a/1352628/11116 – Evan Carroll Aug 27 '18 at 6:52
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
-
4the first one works great! would you mind explaining the logic? im not particularly sure how to interpret the syntax. – user479534 Feb 18 '11 at 4:24
-
45@user8347: Pipe (
|
) the message (echo '...'
) tocat
which uses-
(standard input) as the first file andtodo.txt
as the second.cat
conCATenates multiple files. Send the output (>
) to a file namedtemp
. If there are no errors (&&
) fromcat
then rename (mv
) thetemp
file back to the original file (todo.txt
). – Paused until further notice. Feb 18 '11 at 4:51 -
1@itaifrenkel: I'd have to see what you did, but if
cat
receives a literal backslash n, it won't convert it to a newline. Something else must have done that. Instead ofcat
, try piping intohexdump -C
to see if you're actually sending backslash and n or if it's a newline. You could also trycat -e
to show line endings. – Paused until further notice. Jan 26 '14 at 21:58 -
1Using 2 and 3 (3 seems simpler to me) allows you to prepend text to many files at once. – Felix Jan 27 '14 at 15:24
-
4@Kira: The
1
means do the next command only on line one of the file and thei
command is insert. Look in the man page under the "Addresses" section and in the "Zero- or One- address commands" section. – Paused until further notice. Oct 23 '15 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' && cat todo.txt) > todo.txt
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.
-
1
-
2That might work better (or at all) with double quotes instead of single… – ℝaphink May 22 '13 at 7:25
-
5
-
2Workarounds yield --> cat: <destination filename>: input file is output file ... – ingyhere Jan 9 '15 at 2:10
-
printf would be a lot more consistent across platforms and should generally work more smoothly than echo -e – Peter Berg Feb 17 '18 at 20:10
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
.
You can use Vim in Ex mode:
ex -s -c '1i|task goes here' -c x todo.txt
1
select first linei
insertx
save and close
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.
-
1Say you're out of disk space so that
new_todo.txt
gets written only partially. Your solution appears to lose the original file. – NPE Feb 17 '11 at 10:31 -
-
2@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
rm
of the original file. – Parthian Shot Jul 1 '14 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 Jan 9 '13 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/058 – Rucent88 Jan 14 '13 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
-
Downvoters are welcome to explain their motivation. None of the remaining answers, including the accepted one, do contradict anything in my reply. – jlliagre Apr 12 '16 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 Jun 1 '18 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 Jun 1 '18 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 Jun 2 '18 at 1:11
You can use tee
:
echo 'task goes here' | cat - todo.txt | tee todo.txt
-
3
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]