I've just run the following in bash:
uniq .bash_history > .bash_history
and my history file ended up completely empty.
I guess I need a way to read the whole file before writing to it. How is that done?
PS: I obviously thought of using a temporary file, but I'm looking for a more elegant solution.
should have the desired result. The subshell gets executed before
.bash_history
is opened for writing. As explained in Phil P's answer, by the time.bash_history
is read in the original command, it has already been truncated by the>
operator.I recommend using
sponge
from moreutils. From the manpage:To apply this to your problem, try:
The problem is that your shell is setting up the command pipeline before running the commands. It's not a matter of "input and output", it's that the file's content is already gone before uniq even runs. It goes something like:
>
output file for writing, truncating itThere are various solutions, including the in-place editing and the temporary file usage which others mention, but the key is to understand the problem, what's actually going wrong and why.
Another trick to do this, without using
sponge
, is the following command:This is one of the cheats described in the excellent article “In-place” editing of files on backreference.org.
It basically opens the file for reading, then "removes" it. It's not really removed, though: There's an open file descriptor pointing to it, and as long as that remains open, the file is still around. Then it creates a new file with the same name and writes the unique lines to it.
Disadvantage of this solution: If
uniq
fails for some reason, your history will be gone.use sponge from moreutils
As an interesting tidbit, sed uses a temp file as well (this just does it for you):
Description:
The tempfile
./sedPmPv9z
becomes fd 4, and thefoo
files becomes fd 3. The read operations are on fd 3, and the writes on fd 4 (the temp file). The foo file is then overwritten with the temp file in the rename call.This
sed
script removes adjacent duplicates. With the-i
option, it does the modification in-place. It's from thesed
info
file:Another solution:
A temporary file is pretty much it, unless the command in question happens to support in place editing (
uniq
doesn't - somesed
s do (sed -i
)).You can use Vim in Ex mode:
%
select all lines!
run commandx
save and close