This is probably a complex solution.
I am looking for a simple operator like ">>", but for prepending.
I am afraid it does not exist. I'll have to do something like
mv myfile tmp cat myheader tmp > myfile
Anything smarter?
This is probably a complex solution.
I am looking for a simple operator like ">>", but for prepending.
I am afraid it does not exist. I'll have to do something like
mv myfile tmp cat myheader tmp > myfile
Anything smarter?
This still uses a temp file, but at least it is on one line:
echo "text" | cat - yourfile > /tmp/out && mv /tmp/out yourfile
The hack below was a quick off-the-cuff answer which worked and received lots of upvotes. Then, as the question became more popular and more time passed, people started reporting that it sorta worked but weird things could happen, or it just didn't work at all. Such fun.
I recommend the 'sponge' solution posted by user222 as Sponge is part of 'moreutils' and probably on your system by default.
(echo 'foo' && cat yourfile) | sponge yourfile
The solution below exploits the exact implementation of file descriptors on your system and, because implementation varies significantly between nixes, it's success is entirely system dependent, definitively non-portable, and should not be relied upon for anything even vaguely important. Sponge uses the /tmp filesystem but condenses the task to a single command.
Now, with all that out of the way the original answer was:
Creating another file descriptor for the file (exec 3<> yourfile) thence writing to that (>&3) seems to overcome the read/write on same file dilemma. Works for me on 600K files with awk. However trying the same trick using 'cat' fails.
Passing the prependage as a variable to awk (-v TEXT="$text") overcomes the literal quotes problem which prevents doing this trick with 'sed'.
#!/bin/bash
text="Hello world
What's up?"
exec 3<> yourfile && awk -v TEXT="$text" 'BEGIN {print TEXT}{print}' yourfile >&3
Not possible without a temp file, but here goes a oneliner
{ echo foo; cat oldfile; } > newfile && mv newfile oldfile
You can use other tools such as ed or perl to do it without temp files.
It may be worth noting that it often is a good idea to safely generate the temporary file using a utility like mktemp, at least if the script will ever be executed with root privileges. You could for example do the following (again in bash):
(tmpfile=`mktemp` && { echo "prepended text" | cat - yourfile > $tmpfile && mv $tmpfile yourfile; } )
assuming that the file you want to edit is my.txt
$cat my.txt
this is the regular file
And the file you want to prepend is header
$ cat header
this is the header
Be sure to have a final blank line in the header file.
Now you can prepend it with
$cat header <(cat my.txt) > my.txt
You end up with
$ cat my.txt
this is the header
this is the regular file
As far as I know this only works in 'bash'.
When you start trying to do things that become difficult in shell-script, I would strongly suggest looking into rewriting the script in a "proper" scripting language (Python/Perl/Ruby/etc)
As for prepending a line to a file, it's not possible to do this via piping, as when you do anything like cat blah.txt | grep something > blah.txt, it inadvertently blanks the file. There is a small utility command called sponge you can install (you do cat blah.txt | grep something | sponge blah.txt and it buffers the contents of the file, then writes it to the file). It is similar to a temp file but you dont have to do that explicitly. but I would say that's a "worse" requirement than, say, Perl.
There may be a way to do it via awk, or similar, but if you have to use shell-script, I think a temp file is by far the easiest(/only?) way..
A variant on cb0's solution for "no temp file" to prepend fixed text:
echo "text to prepend" | cat - file_to_be_modified | ( cat > file_to_be_modified )
Again this relies on sub-shell execution - the (..) - to avoid the cat refusing to have the same file for input and output.
Note: Liked this solution. However, in my Mac the original file is lost (thought it shouldn't but it does). That could be fixed by writing your solution as: echo "text to prepend" | cat - file_to_be_modified | cat > tmp_file; mv tmp_file file_to_be_modified
If you're scripting in BASH, actually, you can just issue:
cat - yourfile /tmp/out && mv /tmp/out yourfile
That's actually in the Complex Example you yourself posted in your own question.
The simplest solution I found is:
cat myHeader myFile | tee myFile
or
echo "<line to add>" | cat - myFile | tee myFile
Notes:
echo -n if you want to append just a piece of text (not a full line).&> /dev/null to the end if you don't want to see the output (the generated file).# make it executable (use u+x to allow only current user)
chmod +x cropImage.ts
# append the shebang
echo '#''!'/usr/bin/env ts-node | cat - cropImage.ts | tee cropImage.ts &> /dev/null
# execute it
./cropImage.ts myImage.png
for dash / ash:
echo "hello\n$(cat myfile)" > myfile
example:
$ echo "line" > myfile
$ cat myfile
line
$ echo "line1\n$(cat myfile)" > myfile
$ cat myfile
line1
line