Is there a Unix command to prepend some string data to a text file?
Something like:
prepend "to be prepended" text.txt
Is there a Unix command to prepend some string data to a text file?
Something like:
prepend "to be prepended" text.txt
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 lineIf 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
\n
after prepend; depending on their needs. Neat solution. –
Fourdrinier inFile
include directories in it's path? –
Mahla \n
. Should've read the comments first. Damn. –
Extinctive '1s;^;my-prepended-line\;\n;'
–
Pretty s/^/to be prepended/
(uses slashes) in other sources. Not sure why author uses ;
, it's a bit nonconventional. –
Multicolor ;
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 sed: -e expression #1, char 25: unterminated `s' command
. Possibly related to the comments above r.e. newline character. –
Saundra printf '%s\n%s\n' "to be prepended" "$(cat text.txt)" >text.txt
git config --get-regex $arg | sed -r 's/\t{3}/\\n/g';
and this messes up as it converts the \t
and \n
. –
Perennate echo "to be prepended"$'\n'"$(cat text.txt)"
–
Highpowered 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 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 lineIf 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
\n
after prepend; depending on their needs. Neat solution. –
Fourdrinier inFile
include directories in it's path? –
Mahla \n
. Should've read the comments first. Damn. –
Extinctive '1s;^;my-prepended-line\;\n;'
–
Pretty s/^/to be prepended/
(uses slashes) in other sources. Not sure why author uses ;
, it's a bit nonconventional. –
Multicolor ;
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 sed: -e expression #1, char 25: unterminated `s' command
. Possibly related to the comments above r.e. newline character. –
Saundra 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
Using a here-string - <<<
, you can do:
<<< "to be prepended" < text.txt | sponge text.txt
<<(echo "to be prepended") < text.txt
and <<< "to be prepended" < text.txt
constructions do not work in bash; they require zsh. –
Petticoat fish
? –
Sandry Syntax error: "(" unexpected
with dash. Is this answer bash-specific? –
Luminance ^M
at the end of each line for me –
Mapp 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
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 ( ... )
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 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.
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.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 lines
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..
on its own line.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
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 –
Fourdrinier 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
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...
$$
is insufficient for production code, though (google symlink attack); but it's certainly better than a static file name. –
Frecklefaced mktemp
–
Polluted 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
file.txt
is bigger than can be fitted into the argument list to printf
. –
Flacon 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
sed -i '1ito be prepended' inFile
- or is it only allowed for GNU sed? –
Cheliform 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 Editor's note:
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
.
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 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 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.
If you like vi/vim, this may be more your style.
printf '0i\n%s\n.\nwq\n' prepend-text | ed file
Another fairly straight forward solution is:
$ echo -e "string\n" $(cat file)
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 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.
% echo blaha > blaha
% echo fizz > fizz
% cat blaha fizz > buzz
% cat buzz
blaha
fizz
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.
$ 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
$
With ex
,
ex - $file << PREPEND
-1
i
prepended text
.
wq
PREPEND
The ex
commands are
-1
Go to the very beginning of the filei
Begin insert mode.
End insert modewq
Save (write) and quit# 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
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.
© 2022 - 2024 — McMap. All rights reserved.
<<(echo "to be prepended") < text.txt | sponge text.txt
– Jaeger