tags:

views:

3499

answers:

15

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 $F tmp
 cat header tmp > $F

Anything smarter? (I am not fond of tmp files)

+7  A: 

This still uses a temp file, but at least it is on one line:

echo "text"|cat - yourfile > /tmp/out && mv /tmp/out yourfile

Credit: BASH: Prepend A Text / Lines To a File

Jason Navarrete
+5  A: 

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.

Vinko Vrsalovic
A: 
Tim Kennedy
+5  A: 

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; } )
ehdr
+2  A: 

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

dbr
+7  A: 

No temp files.
One line solution.
Prepend whatever you like.

#!/bin/bash
text="Hello world
What's up?"

exec 3<> yourfile && awk -v TEXT="$text" 'BEGIN {print TEXT}{print}' yourfile >&3

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 nuisance that hinders 'sed'.

John Mee
WARNING: check the output to make sure nothing has changed but the first line. I used this solution, and although it seemed to work, I noticed that some new lines seemed to be added. I don't understand where these came from, so I cannot be sure that this solution really has a problem, but beware.
conradlee
Note in the bash example above that the double quotes span over the carriage return in order to demonstrate prepending multiple lines. Check your shell and text editor are being cooperative. \r\n comes to mind.
John Mee
+2  A: 

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

cb0
why does this work? Do the parentheses cause the middle cat to execute in a separate shell and dump the entire file at once?
Catskul
+1  A: 

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.

A: 
awk 'BEGIN {print}{print "your_prepend"$0}' your_file > output_file
jweyrich
A: 

Bah! No one cared to mention about tac.

endor@grid ~ $ tac --help
Usage: tac [OPTION]... [FILE]...
Write each FILE to standard output, last line first.
With no FILE, or when FILE is -, read standard input.

Mandatory arguments to long options are mandatory for short options too.
  -b, --before             attach the separator before instead of after
  -r, --regex              interpret the separator as a regular expression
  -s, --separator=STRING   use STRING as the separator instead of newline
      --help     display this help and exit
      --version  output version information and exit

Report tac bugs to [email protected]
GNU coreutils home page: <http://www.gnu.org/software/coreutils/&gt;
General help using GNU software: <http://www.gnu.org/gethelp/&gt;
Report tac translation bugs to <http://translationproject.org/team/&gt;
tac could solve the problem, but you just printed out the help for tac... Not particularly useful.
Catskul
A: 

current=cat my_file && echo 'my_string' > my_file && echo $current >> my_file

where "my_file" is the file to prepend "my_string" to.

vinyll
+4  A: 

John Mee: your method is not guaranteed to work, and will probably fail if you prepend more than 4096 byte of stuff (at least that's what happens with gnu awk, but I suppose other implementations will have similar constraints). Not only will it fail in that case, but it will enter an endless loop where it will read its own output, thereby making the file grow until all the available space is filled.

Try it for yourself:

exec 3<>myfile && awk 'BEGIN{for(i=1;i<=1100;i++)print i}{print}' myfile >&3

(warning: kill it after a while or it will fill the filesystem)

Moreover, it's very dangerous to edit files that way, and it's very bad advice, as if something happens while the file is being edited (crash, disk full) you're almost guaranteed to be left with the file in an inconsistent state.

anonymous
A: 
echo '0a
your text here
.
w' | ed some_file

ed is the Standard Editor! http://www.gnu.org/fun/jokes/ed.msg.html

fluffle
A: 

sed -i -e "1s/^/new first line\n/" old_file.txt

shixilun
Oops...that only prepends one line...not an arbitrary file...
shixilun
A: 

The workaround to the overwrite problem is using tee:

cat header main | tee main > /dev/null
Daniel Velkov