196

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

Something like:

prepend "to be prepended" text.txt
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
One Two Three
  • 22,327
  • 24
  • 73
  • 114

21 Answers21

197
printf '%s\n%s\n' "to be prepended" "$(cat text.txt)" >text.txt
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441
shime
  • 8,746
  • 1
  • 30
  • 51
  • 3
    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.txt – Vince Feb 07 '16 at 17:54
  • 46
    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". – Paul Gordon Mar 17 '16 at 15:16
  • @FernandoSilveira Yep. Maybe it's a machine specific thing. I'm on OS X. – Paul Gordon Apr 15 '16 at 22:34
  • 1
    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`. – hIpPy Feb 21 '17 at 20:34
  • 15
    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. – jwd Jun 27 '17 at 23:16
  • 2
    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)"` – jbo5112 Aug 16 '17 at 18:29
  • @Wolverine I wrote a similar solution that is correct on all kind of inputs: https://stackoverflow.com/a/47399735/1091436 (it does no expansions/interpretations) – VasiliNovikov Nov 20 '17 at 19:38
  • 2
    @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). – Charles Duffy Apr 25 '18 at 22:36
  • @CharlesDuffy yeah, I would accept edits that fixed those bugs. – shime Apr 26 '18 at 11:13
  • Maybe I'm showing my age, but wouldn't this cause clobbering? – Keith Tyler Oct 22 '19 at 22:13
  • 1
    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... – Akito Mar 24 '20 at 17:48
173
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, using ; as a command delimiter.
Kyle
  • 1,070
  • 2
  • 13
  • 23
Prince John Wesley
  • 62,492
  • 12
  • 87
  • 94
  • 29
    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. – Levon May 14 '12 at 17:04
  • Yeah, an explanation would be great. Can the `inFile` include directories in it's path? – zakdances Oct 25 '13 at 03:00
  • 4
    @Levon I totally wanted to insert an `\n`. Should've read the comments first. Damn. – chishaku Apr 28 '15 at 19:05
  • 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;'` – SamGamgee Sep 19 '16 at 12:18
  • 9
    Note that '\n' only works for GNU sed, not BSD (such as OSX, FreeBSD, etc.). To be more portable with those, see: https://stackoverflow.com/questions/1421478/how-do-i-use-a-new-line-replacement-in-a-bsd-sed – jwd Jun 27 '17 at 23:19
  • Your delimiter is not very intuitive. Correct answer though – Peter Chaula Jul 24 '17 at 17:26
  • 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. – Mykytak May 31 '21 at 05:15
  • 1
    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). – Mark McDonald Feb 04 '22 at 09:09
  • 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. – Jasha Jun 11 '22 at 22:01
  • Nice solution. I found a bug or a feature depending on your use case. It does not work if the file is empty. – Laurent Lyaudet Jul 27 '23 at 13:56
93

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
Sridhar Sarnobat
  • 25,183
  • 12
  • 93
  • 106
53

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
0xC0000022L
  • 20,597
  • 9
  • 86
  • 152
  • 3
    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. – Sridhar Sarnobat Mar 15 '17 at 22:52
  • 1
    The first method won't preserve file permissions – XMB5 Jun 19 '19 at 18:01
  • The only way to preserve permissions is to use sponge – Sridhar Sarnobat Aug 02 '20 at 04:59
  • 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 `)`. – Alex Shpilkin Jun 07 '21 at 21:48
27

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.
mklement0
  • 382,024
  • 64
  • 607
  • 775
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
Adam
  • 35,919
  • 9
  • 100
  • 137
  • 2
    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 OP – Levon May 14 '12 at 16:55
  • 1
    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 file – One Two Three May 14 '12 at 16:55
  • 1
    and what if I have multiple-line text? How do I put the newline character inthere? – One Two Three May 14 '12 at 16:57
  • This would be cool but bash does not like it: $ echo RewriteBase / | cat - web/.htaccess > web/.htaccess cat: web/.htaccess: input file is output file – geek-merlin Dec 23 '16 at 21:30
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
Community
  • 1
  • 1
ryan
  • 847
  • 1
  • 10
  • 18
  • 1
    this was the only solution working for me. it's funny enough that the name of my server is spongebob. – Ömer An Apr 13 '17 at 09:08
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...

Rob I
  • 5,627
  • 2
  • 21
  • 28
  • 6
    +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. – E Brown May 14 '12 at 17:05
  • 3
    Using `$$` is insufficient for production code, though (google symlink attack); but it's certainly better than a static file name. – tripleee May 14 '12 at 17:12
  • 2
    just use `mktemp` – mewa Mar 11 '19 at 23:29
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
Mert Nuhoglu
  • 9,695
  • 16
  • 79
  • 117
  • Why not `sed -i '1ito be prepended' inFile` - or is it only allowed for GNU sed? – user unknown Apr 11 '18 at 18:39
  • @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.' – Jonathan Leffler May 26 '18 at 22:20
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.

mklement0
  • 382,024
  • 64
  • 607
  • 775
sehari24jam
  • 357
  • 3
  • 3
  • 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. – DC2 Nov 08 '17 at 03:28
  • 6
    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. – Jonathan Leffler Mar 19 '18 at 06:42
  • I too like the simplicity of this answer. It will work on Bourne shell which is not true of my answer – Sridhar Sarnobat May 03 '18 at 17:29
  • 3
    @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. – VasiliNovikov May 26 '18 at 22:16
  • 4
    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. – mklement0 Jan 14 '20 at 19:38
  • 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 @mklement0 comment, then I would say no need to downvote. – Evan Morrison Feb 17 '22 at 19:58
7

Solution:

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

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://stackoverflow.com/a/22607352/1091436

VasiliNovikov
  • 9,681
  • 4
  • 44
  • 62
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.

AlexanderESmith
  • 194
  • 2
  • 4
  • 2
    best answer, I wrote this one-liner to rotate my file using this answer: for i in $( file.txt; done; – Aurangzeb Feb 21 '19 at 09:49
  • 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.txt – madoki Dec 22 '20 at 16:01
2

Another fairly straight forward solution is:

    $ echo -e "string\n" $(cat file)
Pat
  • 353
  • 1
  • 4
  • 15
  • 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; – Craig Wayne May 10 '16 at 11:19
  • 2
    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. – Charles Duffy Apr 25 '18 at 22:34
2

You can do that easily with awk

cat text.txt|awk '{print "to be prepended"$0}'

It seems like the question is about prepending a string to the file not each line of the file, in this case as suggested by Tom Ekberg the following command should be used instead.

awk 'BEGIN{print "to be prepended"} {print $0}' text.txt
Fady Mohamed Othman
  • 1,889
  • 1
  • 15
  • 26
  • The command you gave prepends text to every line of the file. To prepend to the beginning of the file use this awk command: awk 'BEGIN{print "to be prepended"} {print $0}' text.txt. No need to cat and pipe to awk. – Tom Ekberg Mar 21 '22 at 20:17
  • It seems I misunderstood the question, I thought that was what he was looking for, I edited my answer. – Fady Mohamed Othman May 09 '22 at 22:55
2
% echo blaha > blaha
% echo fizz > fizz
% cat blaha fizz > buzz
% cat buzz 
blaha
fizz
Niklas Rosencrantz
  • 25,640
  • 75
  • 229
  • 424
1

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

printf '0i\n%s\n.\nwq\n' prepend-text | ed file
Ian Kelling
  • 9,643
  • 9
  • 35
  • 39
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.

Drakes
  • 23,254
  • 3
  • 51
  • 94
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
$
Markus Dutschke
  • 9,341
  • 4
  • 63
  • 58
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
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
Jim
  • 1
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.

AwesomeBobX64
  • 327
  • 2
  • 4