Unix command to prepend text to a file
Asked Answered
B

20

203

Is there a Unix command to prepend some string data to a text file?

Something like:

prepend "to be prepended" text.txt
Brennabrennan answered 14/5, 2012 at 16:45 Comment(4)
are you looking for a single command, or is a small script/alias command ok?Fourdrinier
If you combine all answers, this is probably the most compact way: <<(echo "to be prepended") < text.txt | sponge text.txtJaeger
This question really brought out the creativity in lots of folks!Brominate
There are a lot of nice answers. But in case you have a complicated string to prepend with nested escaping, you may have a much easier time putting it in a file and prepend a file instead <gist.github.com/LLyaudet/874f2530cec5ec35b5726c0554ef151c>Manservant
T
190
sed -i.old '1s;^;to be prepended;' inFile
  • -i writes the change in place and take a backup if any extension is given. (In this case, .old)
  • 1s;^;to be prepended; substitutes the beginning of the first line by the given replacement string. 1 means act on the first line, s means replace, ; is the delimiter we've selected for s (/ is a common choice as in s/// but any character can be used) and ^ is the regular expression that matches the beginning of a line

If you want to add a new line to the beginning of the file, you need to add \n on the replacement as in:

sed -i.old '1s;^;to be prepended\n;' inFile
Translocate answered 14/5, 2012 at 17:1 Comment(10)
If you could you add some explanation with the command it would be helpful for others not so familiar with the workings of sed. OP may want to insert at \n after prepend; depending on their needs. Neat solution.Fourdrinier
Yeah, an explanation would be great. Can the inFile include directories in it's path?Mahla
@Fourdrinier I totally wanted to insert an \n. Should've read the comments first. Damn.Extinctive
In my case I want to have a semi-colon in the end of the prepended line, so I went with '1s;^;my-prepended-line\;\n;'Pretty
Note that '\n' only works for GNU sed, not BSD (such as OSX, FreeBSD, etc.). To be more portable with those, see: #1421978Motorcade
Your delimiter is not very intuitive. Correct answer thoughHydrofoil
Also you'd probably find s command in a form of s/^/to be prepended/ (uses slashes) in other sources. Not sure why author uses ;, it's a bit nonconventional.Multicolor
I'd guess the author used ; instead of / as you can't have / in the substitution text if it's a delimiter (e.g. if you were prepending a file path).Economical
Sometimes this gives me an error: sed: -e expression #1, char 25: unterminated `s' command. Possibly related to the comments above r.e. newline character.Saundra
Nice solution. I found a bug or a feature depending on your use case. It does not work if the file is empty.Manservant
G
200
printf '%s\n%s\n' "to be prepended" "$(cat text.txt)" >text.txt
Gong answered 14/1, 2015 at 18:52 Comment(13)
In one line and in the original text file! This is the correct simple answer. I suggest the additional new line \n echo -e "to be prepended\n$(cat text.txt)" > text.txtIsodiametric
This solution has problems ... it converts occurrences of "\n" to actual new lines ... problematic if you're prepending to a code file with strings that contain "\n".Knurly
@FernandoSilveira Yep. Maybe it's a machine specific thing. I'm on OS X.Knurly
I had this in my file git config --get-regex $arg | sed -r 's/\t{3}/\\n/g'; and this messes up as it converts the \t and \n.Perennate
This (1) loads the entire file into memory, and (2) sticks the whole file into a single commandline argument. Fine for simple stuff, but beware the limitations.Motorcade
Last I tried, I believe this would break on really large files, where cat fails to read the entire contents of text.txt before it's overwritten. If the -e option on echo is a problem, you can quote a newline in bash or do echo "to be prepended"$'\n'"$(cat text.txt)"Highpowered
@Wolverine I wrote a similar solution that is correct on all kind of inputs: https://mcmap.net/q/126985/-unix-command-to-prepend-text-to-a-file (it does no expansions/interpretations)Thresher
@shime, would you accept an edit that fixed the bugs? printf '%s\n%s\n' "to be prepended" "$(cat text.txt)" >text.txt would avoid the more unfortunate side effects of echo -e. (It's still not particularly good practice -- depends on the shell performing command substitutions before redirections -- but at least with that fix it's not going to be making undesired modifications in the general case with shells that behave in the usual way).Smalls
@CharlesDuffy yeah, I would accept edits that fixed those bugs.Gong
Maybe I'm showing my age, but wouldn't this cause clobbering?Peeved
By far the best answer. I wish the now obselete comment regarding the bugs wouldn't be so prominent to people who don't expand the comment section of this answer...Dimitris
Note that this can lead to IRREVERSIBLE data loss. If/when the process gets killed or there's a power outage, the file will be overwritten but not fully. You might get a partially filled file, a file with zero bytes in it etc.Thresher
By the way, after the edit in 2018 this answer became identical to mine (below). But both are not safe, so looking at the eyes of 10+ years programming experience now, I'd recommend sticking with sed solution https://mcmap.net/q/126985/-unix-command-to-prepend-text-to-a-file or others that create a temp file firstThresher
T
190
sed -i.old '1s;^;to be prepended;' inFile
  • -i writes the change in place and take a backup if any extension is given. (In this case, .old)
  • 1s;^;to be prepended; substitutes the beginning of the first line by the given replacement string. 1 means act on the first line, s means replace, ; is the delimiter we've selected for s (/ is a common choice as in s/// but any character can be used) and ^ is the regular expression that matches the beginning of a line

If you want to add a new line to the beginning of the file, you need to add \n on the replacement as in:

sed -i.old '1s;^;to be prepended\n;' inFile
Translocate answered 14/5, 2012 at 17:1 Comment(10)
If you could you add some explanation with the command it would be helpful for others not so familiar with the workings of sed. OP may want to insert at \n after prepend; depending on their needs. Neat solution.Fourdrinier
Yeah, an explanation would be great. Can the inFile include directories in it's path?Mahla
@Fourdrinier I totally wanted to insert an \n. Should've read the comments first. Damn.Extinctive
In my case I want to have a semi-colon in the end of the prepended line, so I went with '1s;^;my-prepended-line\;\n;'Pretty
Note that '\n' only works for GNU sed, not BSD (such as OSX, FreeBSD, etc.). To be more portable with those, see: #1421978Motorcade
Your delimiter is not very intuitive. Correct answer thoughHydrofoil
Also you'd probably find s command in a form of s/^/to be prepended/ (uses slashes) in other sources. Not sure why author uses ;, it's a bit nonconventional.Multicolor
I'd guess the author used ; instead of / as you can't have / in the substitution text if it's a delimiter (e.g. if you were prepending a file path).Economical
Sometimes this gives me an error: sed: -e expression #1, char 25: unterminated `s' command. Possibly related to the comments above r.e. newline character.Saundra
Nice solution. I found a bug or a feature depending on your use case. It does not work if the file is empty.Manservant
J
94

Process Substitution

I'm surprised no one mentioned this.

cat <(echo "before") text.txt > newfile.txt

which is arguably more natural than the accepted answer (printing something and piping it into a substitution command is lexicographically counter-intuitive).

...and hijacking what ryan said above, with sponge you don't need a temporary file:

sudo apt-get install moreutils
<<(echo "to be prepended") < text.txt | sponge text.txt

EDIT: Looks like this doesn't work in Bourne Shell /bin/sh


Here String (zsh only)

Using a here-string - <<<, you can do:

<<< "to be prepended" < text.txt | sponge text.txt
Jaeger answered 15/3, 2017 at 22:55 Comment(11)
This answer is the winner for me. Simple and achievable with builtins only. Good work!Marozas
@Marozas I agree! This helped a lot, thank you Sridhar-Sarnobat.Manstopper
The problem is that there is 1 file, not 2 files. So the first line of the answer doesn't really work. The last line might work (but it requires sponge).Thresher
Yes (though that's not a problem specifically with my answer, that's an inconvenience of the shell). Feel free to update my answer if it helps clarify how to make use of it.Jaeger
What does sponge do ?Mccreery
It prevents the input file from getting erased when it is both read and written to.Jaeger
Your <<(echo "to be prepended") < text.txt and <<< "to be prepended" < text.txt constructions do not work in bash; they require zsh.Petticoat
is there a similar one to fish?Sandry
I'm getting Syntax error: "(" unexpected with dash. Is this answer bash-specific?Luminance
Note that this can lead to IRREVERSIBLE data loss. If/when the process gets killed or there's a power outage, the file will be overwritten but not fully. You might get a partially filled file, a file with zero bytes in it etc.Thresher
This one added ^M at the end of each line for meMapp
P
54

This is one possibility:

(echo "to be prepended"; cat text.txt) > newfile.txt

you'll probably not easily get around an intermediate file.

Alternatives (can be cumbersome with shell escaping):

sed -i '0,/^/s//to be prepended/' text.txt
Proofread answered 14/5, 2012 at 16:50 Comment(4)
Strategy 1) is the most intuitive of any answers here, I feel. And a slight variation: cat <(echo "to be prepended") text.txt > newfile.txt . Come to think of it, I'm not sure that mine is related, so Im posting a separate answer.Jaeger
The first method won't preserve file permissionsWool
The only way to preserve permissions is to use spongeJaeger
Spawning a subshell with ( ... ) is unnecessary: the shell is absolutely fine just redirecting the output of a group command { ... }. Wart alert: the shell grammar requires a terminating semicolon or newline before } but not before ).Kimono
B
29

If it's acceptable to replace the input file:

Note:

  • Doing so may have unexpected side effects, notably potentially replacing a symlink with a regular file, ending up with different permissions on the file, and changing the file's creation (birth) date.

  • sed -i, as in Prince John Wesley's answer, tries to at least restore the original permissions, but the other limitations apply as well.

Here's a simple alternative that uses a temporary file (it avoids reading the whole input file into memory the way that shime's solution does):

{ printf 'to be prepended'; cat text.txt; } > tmp.txt && mv tmp.txt text.txt

Using a group command ({ ...; ...; }) is slightly more efficient than using a subshell ((...; ...)), as in 0xC0000022L's solution.

The advantages are:

  • It's easy to control whether the new text should be directly prepended to the first line or whether it should be inserted as new line(s) (simply append \n to the printf argument).

  • Unlike the sed solution, it works if the input file is empty (0 bytes).


The sed solution can be simplified if the intent is to prepend one or more whole lines to the existing content (assuming the input file is non-empty):

sed's i function inserts whole lines:

With GNU sed:

# Prepends 'to be prepended' *followed by a newline*, i.e. inserts a new line.
# To prepend multiple lines, use '\n' as part of the text.
# -i.old creates a backup of the input file with extension '.old'
sed -i.old '1 i\to be prepended' inFile

A portable variant that also works with macOS / BSD sed:

# Prepends 'to be prepended' *followed by a newline*
# To prepend multiple lines, escape the ends of intermediate
# lines with '\'
sed -i.old -e '1 i\
to be prepended' inFile

Note that the literal newline after the \ is required.


If the input file must be edited in place (preserving its inode with all its attributes):

Using the venerable ed POSIX utility:

Note:

  • ed invariably reads the input file as a whole into memory first.

To prepend directly to the first line (as with sed, this won't work if the input file is completely empty (0 bytes)):

ed -s text.txt <<EOF
1 s/^/to be prepended/
w
EOF
  • -s suppressed ed's status messages.
  • Note how the commands are provided to ed as a multi-line here-document (<<EOF\n...\nEOF), i.e., via stdin; by default string expansion is performed in such documents (shell variables are interpolated); quote the opening delimiter to suppress that (e.g., <<'EOF').
  • 1 makes the 1st line the current line
  • function s performs a regex-based string substitution on the current line, as in sed; you may include literal newlines in the substitution text, but they must be \-escaped.
  • w writes the result back to the input file (for testing, replace w with ,p to only print the result, without modifying the input file).

To prepend one or more whole lines:

As with sed, the i function invariably adds a trailing newline to the text to be inserted.

ed -s text.txt <<EOF
0 i
line 1
line 2
.
w
EOF
  • 0 i makes 0 (the beginning of the file) the current line and starts insert mode (i); note that line numbers are otherwise 1-based.
  • The following lines are the text to insert before the current line, terminated with . on its own line.
Blade answered 22/5, 2015 at 3:0 Comment(0)
S
23

This will work to form the output. The - means standard input, which is provide via the pipe from echo.

echo -e "to be prepended \n another line" | cat - text.txt

To rewrite the file a temporary file is required as cannot pipe back into the input file.

echo "to be prepended" | cat - text.txt > text.txt.tmp
mv text.txt.tmp text.txt
Schrimsher answered 14/5, 2012 at 16:52 Comment(4)
this doesn't prepend the text to the file text.txt though as requested, but displays it on stdout. The output could be funneled into a different file if that works for the OPFourdrinier
this would definitely print the "to be prepended" and then the content of "text.txt". But I'd like the text to be prepended to the fileBrennabrennan
and what if I have multiple-line text? How do I put the newline character inthere?Brennabrennan
This would be cool but bash does not like it: $ echo RewriteBase / | cat - web/.htaccess > web/.htaccess cat: web/.htaccess: input file is output fileWinery
J
14

Prefer Adam's answer

We can make it easier to use sponge. Now we don't need to create a temporary file and rename it by

echo -e "to be prepended \n another line" | cat - text.txt | sponge text.txt
Judgemade answered 25/12, 2015 at 8:32 Comment(1)
this was the only solution working for me. it's funny enough that the name of my server is spongebob.Scalawag
A
8

Probably nothing built-in, but you could write your own pretty easily, like this:

#!/bin/bash
echo -n "$1" > /tmp/tmpfile.$$
cat "$2" >> /tmp/tmpfile.$$
mv /tmp/tmpfile.$$ "$2"

Something like that at least...

Affectionate answered 14/5, 2012 at 16:49 Comment(3)
+1 for using $$ (the current PID) as part of the temp filename. If creating a script for general use, this will prevent another user from potentially overwriting the temp file by running the same script at the same time.Shade
Using $$ is insufficient for production code, though (google symlink attack); but it's certainly better than a static file name.Frecklefaced
just use mktempPolluted
T
8

Solution for smaller files:

printf '%s\n%s' 'text to prepend' "$(cat file.txt)" > file.txt

Note that this is safe on all kind of inputs, because there are no expansions. For example, if you want to prepend !@#$%^&*()ugly text\n\t\n, it will just work:

printf '%s\n%s' '!@#$%^&*()ugly text\n\t\n' "$(cat file.txt)" > file.txt

UPDATE 2023: Re-reading my own answer. It's important to not that this solution is NOT SAFE if e.g. you have a power outage while the file was being written to disk. The reason is that file content filling is not atomic in the filesystem, so you may be interrupted while you've already read the file, started writing to the old address (thus beginning a full overwrite) but not finishing it. This problem also applies to all solutions that do not create the intermediate file, included the currently most upvoted solution.

The last part left for consideration is whitespace removal at end of file during command substitution "$(cat file.txt)". All work-arounds for this are relatively complex. If you want to preserve newlines at end of file.txt, see this: https://mcmap.net/q/54052/-how-to-read-a-file-into-a-variable-in-shell

Thresher answered 20/11, 2017 at 19:36 Comment(2)
Love it. Super portable and doesn't need to generate a new file. Thanks!Mickens
The only problem with this comes if the contents of file.txt is bigger than can be fitted into the argument list to printf.Flacon
D
7

Another way using sed:

sed -i.old '1 {i to be prepended
}' inFile

If the line to be prepended is multiline:

sed -i.old '1 {i\ 
to be prepended\
multiline
}' inFile
Dorris answered 6/10, 2014 at 12:48 Comment(2)
Why not sed -i '1ito be prepended' inFile - or is it only allowed for GNU sed?Cheliform
@userunknown: In standard sed, the i command is followed by a backslash and a newline, and each line of input except the last ends with a backslash too. GNU sed allows shorthands along the lines you ask about, but it is only GNU sed that does so.'Flacon
F
7

Editor's note:

  • This command will result in data loss if the input file happens to be larger than your system's pipeline buffer size, which is typically 64 KB nowadays. See the comments for details.

In some circumstances prepended text may available only from stdin. Then this combination shall work.

echo "to be prepended" | cat - text.txt | tee text.txt

If you want to omit tee output, then append > /dev/null.

Fernandez answered 11/8, 2017 at 1:29 Comment(6)
I really like this answer. It's a very clean and clear way to do it. The use of tee is a natural solution to the problem of trying to cat output to the input file. Also no hacks with renaming. Better than the highest voted solution right now, as it doesn't create a new file. This is basically the closest you'll get to >> for prepending instead of appending.Cheviot
Is there a guarantee that tee doesn't clobber text.txt before cat gets to read it? I think not — which would make this solution dangerous to the health of the file.Flacon
I too like the simplicity of this answer. It will work on Bourne shell which is not true of my answerJaeger
@JonathanLeffler probably no. I tried this code to test: lenA=1000000; yes a | head -c $lenA > a.txt; lenB=10000; b=$(yes b | head -c $lenB); echo "$b" | cat - a.txt | tee a.txt > /dev/null. If lenA is 1000000 (1Mb file) and lenB is 10000 (10Kb text prepend), then file "a.txt" is overwritten with 20Kb of "b" letters. This is totally broken. Now, if you use 1Mb a.txt and 1Mb text to prepend, tee goes into a loop generating 7Gb+ file, I had to stop the command. So, it's obvious that the result is unpredictable for large sizes. I have no information whether it should work on small sizes.Thresher
To put it in conceptual terms: This command will result in data loss if the input file happens to be larger than your system's pipeline buffer size, which is typically 64 KB nowadays.Blade
Unfortunately the 'Suggested edit queue is full' -- if we can't put warnings on this answer, it should be downvoted since it has the potential to destroy data. If the Original answer can include @Blade comment, then I would say no need to downvote.Shakitashako
L
7

As tested in Bash (in Ubuntu), if starting with a test file via;

echo "Original Line" > test_file.txt

you can execute;

echo "$(echo "New Line"; cat test_file.txt)" > test_file.txt

or, if the version of bash is too old for $(), you can use backticks;

echo "`echo "New Line"; cat test_file.txt`" > test_file.txt

and receive the following contents of "test_file.txt";

New Line
Original Line

No intermediary file, just bash/echo.

Leventis answered 7/2, 2019 at 19:15 Comment(2)
best answer, I wrote this one-liner to rotate my file using this answer: for i in $(<file2.txt); do echo "$(echo $i; cat file.txt)" > file.txt; done;Plesiosaur
Best answer. You might want to use the '>|' operator (this is not a pipe...), to overwrite the text file even if the shell has 'noclobber' set. So: echo "$(echo "New Line"; cat test_file.txt)" >| test_file.txtSidwohl
T
1

If you like vi/vim, this may be more your style.

printf '0i\n%s\n.\nwq\n' prepend-text | ed file
Tumult answered 21/1, 2015 at 23:9 Comment(0)
H
1

Another fairly straight forward solution is:

    $ echo -e "string\n" $(cat file)
Hacker answered 16/7, 2015 at 14:21 Comment(2)
This worked for me... just a slight modification, in order to maintain multilines from the input file you gotta wrap it in quotes so something like this: echo -e "string\n" "$(cat ~/file.txt)" > ~/file.txt;Norty
Not having the cat parameter expansion in double quotes is a pretty good reason for a downvote. This code is splitting the file's contents into words, and passing each of those words as a separate argument to echo. You lose the original argument boundaries, you lose newlines/tabs/etc., and strings like \t and \n in the original text are replaced with tabs and newlines instead of being kept as they were.Smalls
M
1

For future readers who want to append one or more lines of text (with variables or even subshell code) and keep it readable and formatted, you may enjoy this:

echo "Lonely string" > my-file.txt

Then run

cat <<EOF > my-file.txt
Hello, there!

$(cat my-file.txt)
EOF

Results of cat my-file.txt:

Hello, there!

Lonely string

This works because the read of my-file.txt happens first and in a subshell. I use this trick all the time to append important rules to config files in Docker containers rather than copy over entire config files.

Mastin answered 17/6, 2021 at 21:40 Comment(1)
My favorite answer of the bunchGereron
P
1
% echo blaha > blaha
% echo fizz > fizz
% cat blaha fizz > buzz
% cat buzz 
blaha
fizz
Pilsner answered 14/11, 2021 at 18:57 Comment(0)
T
1

you can use variables

Even though a bunsh of answers here work pretty well, I want to contribute this one-liner, just for completeness. At least it is easy to keep in mind and maybe contributes to some general understanding of bash for some people.

PREPEND="new line 1"; FILE="text.txt"; printf "${PREPEND}\n`cat $FILE`" > $FILE

In this snippe just replace text.txt with the textfile you want to prepend to and new line 1 with the text to prepend.

example

$ printf "old line 1\nold line 2" > text.txt
$ cat text.txt; echo ""
old line 1
old line 2
$ PREPEND="new line 1"; FILE="text.txt"; printf "${PREPEND}\n`cat $FILE`" > $FILE 
$ cat text.txt; echo ""
new line 1
old line 1
old line 2
$
Tenorite answered 21/7, 2022 at 13:54 Comment(0)
T
1

With ex,

ex - $file << PREPEND
-1
i
prepended text
.
wq
PREPEND

The ex commands are

  • -1 Go to the very beginning of the file
  • i Begin insert mode
  • . End insert mode
  • wq Save (write) and quit
Tola answered 3/11, 2022 at 2:9 Comment(0)
O
0
# create a file with content..
echo foo > /tmp/foo
# prepend a line containing "jim" to the file
sed -i "1s/^/jim\n/" /tmp/foo
# verify the content of the file has the new line prepened to it
cat /tmp/foo
Oxidate answered 16/4, 2015 at 21:34 Comment(0)
D
0

I'd recommend defining a function and then importing and using that where needed.

prepend_to_file() { 
    file=$1
    text=$2

    if ! [[ -f $file ]] then
        touch $file
    fi

    echo "$text" | cat - $file > $file.new

    mv -f $file.new $file
}

Then use it like so:

prepend_to_file test.txt "This is first"
prepend_to_file test.txt "This is second"

Your file contents will then be:

This is second
This is first

I'm about to use this approach for implementing a change log updater.

Derrickderriey answered 21/6, 2018 at 16:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.