Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unix: How can I prepend output to a file?

Specifically, I'm using a combination of >> and tee in a custom alias to store new Homebrew updates in a text file, as well as output on screen:

alias bu="echo `date "+%Y-%m-%d at %H:%M"` \
    >> ~/Documents/Homebrew\ Updates.txt && \
    brew update | tee -a ~/Documents/Homebrew\ Updates.txt"

Question: What if I wish to prepend this output in my textfile, i.e. placed at the beginning of the file as opposed to appending it to the end?


Edit1: As someone reported in the answers below, the use of temp files might be a good approach, which at least helped me partially:

targetLog="~/Documents/Homebrew\ Updates.txt"
alias bu="(brew update | cat - $targetLog \
> /tmp/out1 && mv /tmp/out1 $targetLog \
&& echo `date "+%Y-%m-%d at %H:%M":%S` | \
cat - $targetLog > /tmp/out2 \
&& mv /tmp/out2 $targetLog)"

But the problem is the output to STDOUT (previously made possible by tee), which I'm not sure can be incorporated in this tempfile approach …?

like image 368
Henrik Avatar asked Oct 18 '11 12:10

Henrik


3 Answers

sed will happily do that for you, using -i to edit in place, eg.

sed -i -e "1i `date "+%Y-%m-%d at %H:%M"`" some_file
like image 110
Hasturkun Avatar answered Oct 18 '22 20:10

Hasturkun


This works by creating an output file:

Let's say we have the initial contents on file.txt

echo "first line" > file.txt          
echo "second line" >> file.txt

So, file.txt is our 'bottom' text file. Now prepend into a new 'output' file

echo "add new first line" | cat - file.txt > output.txt # <--- Just this command

Now, output has the contents the way we want. If you need your old name:

mv output.txt file.txt
cat file.txt
like image 9
mimoralea Avatar answered Oct 18 '22 18:10

mimoralea


The only simple and safe way to modify an input file using bash tools, is to use a temp file, eg. sed -i uses a temp file behind the scenes (but to be robust sed needs more).

Some of the methods used have a subtle "can break things" trap, when, rather than running your command on the real data file, you run it on a symbolic link (to the file you intend to modify). Unless catered for correctly, this can break the link and convert it into a real file which receives the mods and leaves the original real file without the intended mods and without the symlink (no error exit-code results)

To avoid this with sed, you need to use the --follow-symlinks option.
For other methods, just be aware that it needs to follow symlinks (when you act on such a link)
Using a temp file, then rm temp file works only if "file" is not a symlink.

One safe way is to use sponge from package moreutils

Unlike a shell redirect, sponge soaks up all its input before opening the output file. This allows for constructing pipelines that read from and write to the same file.

sponge is a good general way to handle this type of situation.

Here is an example, using sponge

hbu=~/'Documents/Homebrew Updates.txt'
{ date "+%Y-%m-%d at %H:%M"; cat "$hbu"; } | sponge "$hbu"
like image 5
Peter.O Avatar answered Oct 18 '22 20:10

Peter.O