我只是在bash中运行以下命令:
uniq .bash_history > .bash_history
我的历史记录文件最终完全为空。
我想我需要一种在写入文件之前先读取整个文件的方法。怎么做?
PS:我显然想过要使用一个临时文件,但是我正在寻找一个更优雅的解决方案。
bash
如果将HISTCONTROL设置为包含忽略的内容,则不会在其历史记录中放置连续的重复项;请参阅联机帮助页。
我只是在bash中运行以下命令:
uniq .bash_history > .bash_history
我的历史记录文件最终完全为空。
我想我需要一种在写入文件之前先读取整个文件的方法。怎么做?
PS:我显然想过要使用一个临时文件,但是我正在寻找一个更优雅的解决方案。
bash
如果将HISTCONTROL设置为包含忽略的内容,则不会在其历史记录中放置连续的重复项;请参阅联机帮助页。
Answers:
我建议sponge
从moreutils使用。从联机帮助页:
DESCRIPTION
sponge reads standard input and writes it out to the specified file. 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.
要将其应用于您的问题,请尝试:
uniq .bash_history | sponge .bash_history
我只想提供一个简单但不使用海绵的答案(因为它通常不包含在轻量级环境中)。
echo "$(uniq .bash_history)" > .bash_history
应该具有预期的结果。在打开.bash_history进行写入之前,将执行该子Shell。正如Phil P的答案所解释的,当在原始命令中读取.bash_history时,它已经被'>'运算符截断了。
$()
由于某些转义问题,我不得不使用subshell 而不是反引号。
echo "$(fmt -p '# ' -w 50 readme.txt)" > readme.txt
今天用这个答案做。一直在寻找一种优雅的解决方案。非常感谢,@ Hart Simha!
使用moreutils的海绵
uniq .bash_history | sponge .bash_history
该sed
脚本删除相邻的重复项。使用该-i
选项,它就地进行修改。来自sed
info
文件:
sed -i 'h;:b;$b;N;/^\(.*\)\n\1$/ {g;bb};$b;P;D' .bash_history
strace
插图的答案(并不是真的很重要):-)
process input > tmp && mv tmp input
比起使用sed
欺骗手段来避免临时文件,这样的操作要简单得多,而且可读性强,如果失败了,它将不会覆盖我的原始文件(我不知道是否sed -i
会正常失败-我会认为会的)。此外,使用output-to-temp-file方法可以做很多事情,如果没有比此sed
脚本还要涉及更多的事情,就无法就地完成。我知道您知道所有这一切,但可能会使围观者受益。
作为一个有趣的花絮,sed也使用一个临时文件(这正是为您做的):
$ strace sed -i 's/foo/bar/g' foo
open("foo", O_RDONLY|O_LARGEFILE) = 3
...
open("./sedPmPv9z", O_RDWR|O_CREAT|O_EXCL|O_LARGEFILE, 0600) = 4
...
read(3, "foo\n"..., 4096) = 4
write(4, "bar\n"..., 4) = 4
read(3, ""..., 4096) = 0
close(3) = 0
close(4) = 0
rename("./sedPmPv9z", "foo") = 0
close(1) = 0
close(2) = 0
说明:
临时文件./sedPmPv9z
变成fd 4,foo
文件变成fd3。读操作在fd 3上,写在fd 4(临时文件)上。然后在重命名调用中用临时文件覆盖foo文件。
临时文件几乎就是临时文件,除非有问题的命令碰巧支持就地编辑(uniq
不是-某些文件支持sed
(sed -i
))。
您也可以使用tee,并使用uniq输出作为输入:
uniq .bash_history | tee .bash_history