How do I add text to the beginning of a file in Bash?

306

94

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).

user479534

Posted 2011-02-17T03:21:12.893

Reputation: 3 271

2

There if no efficient way to do that for large files: http://unix.stackexchange.com/questions/87772/add-lines-to-the-beginning-and-end-of-the-huge-file

– Ciro Santilli 新疆改造中心法轮功六四事件 – 2015-11-22T19:45:30.780

@ould you take a look at this method https://superuser.com/a/1352628/11116

– Evan Carroll – 2018-08-27T06:52:00.110

Answers

396

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

Paused until further notice.

Posted 2011-02-17T03:21:12.893

Reputation: 86 075

cat can be freely used to concatenate binaries. Note how split and join do entirely different things; cat is the companion to split, not join. And that means, if your cat messed up \n your implementation is horribly broken. – SF. – 2014-08-05T10:14:24.943

Using the third option, I got the task in the file every other line....so I guess it put it on the first line, but didn't do so well for the rest of the file for my use case. – onaclov2000 – 2015-08-22T17:15:23.420

@onaclov2000: The second sed command in my answer says to insert a line of text before line 1. I'd have to see the command you used to know why it inserted it on every other line. You probably omitted the address (1). – Paused until further notice. – 2015-08-22T19:28:05.697

1I didn't know about this possible syntax in sed's last example, can someone tell me what is the name of this type of syntax so I can search more about it? – Kira – 2015-10-22T19:04:44.003

4@Kira: The 1 means do the next command only on line one of the file and the i 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. – 2015-10-23T16:33:52.903

The echo line works perfect for DD-WRT firmware for a script I run. Using sed and putting in the \t for a tab, puts just a t then the next part. Where the echo line actually puts the tab in instead of the t. Thank you! – Terrance – 2016-07-20T20:37:59.327

1@Terrance: If your sed command looks something like this (inserting a leading tab): '1i\tSome Text', you may need to double the backslash. Otherwise, your dialect of sed may not recognize \t as tab. – Paused until further notice. – 2016-07-20T21:07:06.137

@DennisWilliamson Unfortunately the sed doesn't recognize the \t as tab. I forgot to say that I had to do it as echo -e to make it work with the tab. – Terrance – 2016-07-20T21:14:10.600

The sed versions don't seem to work on an empty file. – Simon Lindholm – 2019-03-13T15:06:24.653

For what it's worth the cat version is around 3 times faster than the sed ones. At least on the test in did on a ~4GB file. – Xælias – 2020-01-31T15:20:59.813

6the first one works great! would you mind explaining the logic? im not particularly sure how to interpret the syntax. – user479534 – 2011-02-18T04:24:36.143

47@user8347: Pipe (|) the message (echo '...') to cat which uses - (standard input) as the first file and todo.txt as the second. cat conCATenates multiple files. Send the output (>) to a file named temp. If there are no errors (&&) from cat then rename (mv) the temp file back to the original file (todo.txt). – Paused until further notice. – 2011-02-18T04:51:26.723

cat could be a problem if cat encounters \n – itaifrenkel – 2014-01-26T08:50:28.813

@itaifrenkel: In what way? – Paused until further notice. – 2014-01-26T12:16:14.410

Instead of printing \n it may print a newline. Happened to me. Used the sed version just fine instead – itaifrenkel – 2014-01-26T21:37:56.593

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 of cat, try piping into hexdump -C to see if you're actually sending backslash and n or if it's a newline. You could also try cat -e to show line endings. – Paused until further notice. – 2014-01-26T21:58:05.603

1Using 2 and 3 (3 seems simpler to me) allows you to prepend text to many files at once. – Felix – 2014-01-27T15:24:15.190

81

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.

John Alberts

Posted 2011-02-17T03:21:12.893

Reputation: 911

2Workarounds yield --> cat: <destination filename>: input file is output file ... – ingyhere – 2015-01-09T02:10:05.227

printf would be a lot more consistent across platforms and should generally work more smoothly than echo -e – Peter Berg – 2018-02-17T20:10:51.070

Warning: The last one overwrites each file with the echo (or printf) statement. – xizdaqrian – 2018-03-29T05:31:52.910

I'm not sure this works with large files, bash string size is probably limited to a few MB. – jesjimher – 2018-05-10T11:06:43.653

Warning: replaces occurrences of '\n' with actual newlines – Vivek Kodira – 2019-02-20T09:17:52.830

1I don't get $(...) executed at all. – SCL – 2013-02-22T17:10:23.993

2That might work better (or at all) with double quotes instead of single… – ℝaphink – 2013-05-22T07:25:01.627

5Won't the -e also convert escape sequences inside todo.txt? – mk12 – 2013-09-02T21:23:29.893

28

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.

slhck

Posted 2011-02-17T03:21:12.893

Reputation: 182 472

I would go for (echo 'ble'; cat todo.txt) :-) – Ciro Santilli 新疆改造中心法轮功六四事件 – 2018-12-04T09:37:35.987

12

You can use Vim in Ex mode:

ex -s -c '1i|task goes here' -c x todo.txt
  1. 1 select first line

  2. i insert

  3. x save and close

Steven Penny

Posted 2011-02-17T03:21:12.893

Reputation: 7 294

5

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.

Keith

Posted 2011-02-17T03:21:12.893

Reputation: 7 263

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 – 2014-07-01T20:23:30.273

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 – 2011-02-17T10:31:29.280

Who runs out of disk space? ;-) It's just a simple example. – Keith – 2011-02-17T10:33:56.277

3

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

jlliagre

Posted 2011-02-17T03:21:12.893

Reputation: 12 469

Downvoters are welcome to explain their motivation. None of the remaining answers, including the accepted one, do contradict anything in my reply. – jlliagre – 2016-04-12T13:13:53.420

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 – 2018-06-01T09:20:36.943

This is not working for me. echo HOME=\"/g/Users/timregan/\" | cat - 'F:\Program Files\Git\etc\profile' works but cat <echo HOME=\"/g/Users/timregan/\" 'F:\Program Files\Git\etc\profile' gives the error "echo: No such file or directory" – dumbledad – 2018-06-01T09:35:16.107

@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. Try cat <(echo HOME=\"/g/Users/timregan/\") 'F:\Program Files\Git\etc\profile' – jlliagre – 2018-06-02T01:11:04.287

3

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.

Rucent88

Posted 2011-02-17T03:21:12.893

Reputation: 632

Just to ask the obvious question: Where's the character limit of shell variables? – nixda – 2013-01-09T22:59:50.313

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 http://mywiki.wooledge.org/BashFAQ/058

– Rucent88 – 2013-01-14T01:48:04.393

1

You can use tee:

echo 'task goes here' | cat - todo.txt | tee todo.txt

Radu Rădeanu

Posted 2011-02-17T03:21:12.893

Reputation: 203

3

No you cannot http://askubuntu.com/a/752451

– Steven Penny – 2016-04-11T00:25:19.290

0

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]

J MADISON

Posted 2011-02-17T03:21:12.893

Reputation: 219