Unix: How can I prepend output to a file?
Asked Answered
D

7

27

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 …?

Ding answered 18/10, 2011 at 12:12 Comment(1)
this post should help you, use search before posting new questionWernher
C
26

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
Curtilage answered 18/10, 2011 at 12:27 Comment(2)
I think this may not work for empty files.Babarababassu
@Osyotr: True, sed won't do anything for a 0-byte file.Curtilage
S
19

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
Scale answered 22/7, 2014 at 16:10 Comment(1)
sed behaves differently between mac and linux so this version is good if you need to avoid sed for that reason.Attitudinarian
R
6

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"
Rick answered 18/10, 2011 at 13:50 Comment(5)
How/where do I get sponge if I'm using Darwin UNIX (OS X)?Ding
How does changing the inode make a symbolic link invalid? I assume you mean it invalidates hard links.Danish
@Mark Edgar: I had it around the wrong way. The problem arises when you act on the link.. I've ammended the answer..... @hced: I have no idea about Darwin specifically, but here is a link to moreutils...Rick
@hced If you have MacPorts, then you can use port install moreutilsDishwasher
homebrew has sponge in the moreutils package as well. brew install moreutils [2.2.3] ==> Downloading homebrew.bintray.com/bottles/…Mussman
G
2

Simplest way IMO would be to use echo and cat:

echo "Prepend" | cat - inputfile > outputfile

Or for your example basically replace the tee -a ~/Documents/Homebrew\ Updates.txt with cat - ~/Documents/Homebrew\ Updates.txt > ~/Documents/Homebrew\ Updates.txt

Edit: As stated by hasturkun this won't work, try:

echo "Prepend" | cat - file | tee file

But this isn't the most efficient way of doing it any more...

Gredel answered 18/10, 2011 at 12:30 Comment(4)
This will not work. It will begin by truncating the file, then trying to read from it.Curtilage
I've used it before (The good old cat image.jpg secret.zip > image.jpg trick) Edit: Whoops, must have used a different one, yeah scratch this answer.Gredel
Try it, the shell first opens the file for writing, truncating it, then runs cat (which on my system causes cat to complain that the input file is the output file), causing the file to only contain the contents of the second fileCurtilage
This works. Test it yourself on your own. Some people like to comment without trying for themselves.Scale
O
2

Similar to the accepted answer, however if you are coming here because you want to prepend to the first line - rather than prepend an entirely new line - then use this command.

sed -i "1 s/^/string_replacement/" some_file

The -i flag will do a replacement within the file (rather than creating a new file).
Then the 1 will only do the replacement on line 1.

Finally, the s command is used which has the following syntax s/find/replacement/flags.
In our case we don't need any flags. The ^ is called a caret and it is used to represent the very start of a string.

Otocyst answered 24/11, 2022 at 19:12 Comment(0)
S
1

Try this http://www.unix.com/shell-programming-scripting/42200-add-text-beginning-file.html There is no direct operator or command AFAIK.You use echo, cat, and mv to get the effect.

Semivowel answered 18/10, 2011 at 12:19 Comment(1)
You may want to include at least some of the information behind that link, you may want to see How to Answer and meta.stackexchange.com/q/8231/20270Curtilage
D
1
{ date; brew update |tee /dev/tty; cat updates.txt; } >updates.txt.new
mv updates.txt.new updates.txt

I've no idea why you want to do this. It's pretty standard that logs like this have later entries appearing, well, later in the file.

Danish answered 18/10, 2011 at 20:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.