bash
が重複するコマンドを履歴に保存しないようにしようとしています。これが私が持っているものです:
shopt -s histappend
export HISTIGNORE='&:ls:cd ~:cd ..:[bf]g:exit:h:history'
export HISTCONTROL=erasedups
export Prompt_COMMAND='history -a'
ログインしていて、.bash_history
がメモリにある場合、これは正常に機能します。例えば:
$ history
1 vi .bashrc
2 vi .alias
3 cd /cygdrive
4 cd ~jplemme
5 vi .bashrc
6 vi .alias
$ vi .bashrc
$ history
1 vi .alias
2 cd /cygdrive
3 cd ~jplemme
4 vi .alias
5 vi .bashrc
$ vi .alias
$ history
1 cd /cygdrive
2 cd ~jplemme
3 vi .bashrc
4 vi .alias
$ exit
しかし、再度ログインすると、履歴ファイルは次のようになります。
$ history
1 vi .bashrc
2 vi .alias
3 cd /cygdrive
4 cd ~jplemme
5 vi .bashrc
6 vi .alias
7 vi .bashrc
8 vi .alias
私は何が間違っているのですか?
編集:shopt
およびPrompt_COMMAND
行を.bashrc
から削除しても問題は解決しません。
私の知る限り、あなたがやりたいことをすることは不可能です。これはbashの履歴処理のバグであり、改善される可能性があります。
export HISTCONTROL=ignoreboth:erasedups # no duplicate entries
shopt -s histappend # append history file
export Prompt_COMMAND="history -a" # update histfile after every command
これにより、メモリ内の履歴が一意に保たれますが、複数のセッションの履歴が同じファイルに保存されますが、ファイル自体の履歴は一意に保たれません。 history -a
は、直前のコマンドと同じでない限り、新しいコマンドをファイルに書き込みます。 erasedups
設定がメモリで行うように、完全な重複排除は行われません。
この愚かさの動作を確認するには、新しいターミナルセッションを開始し、履歴を調べると、ls
などのエントリが繰り返されます。ここでls
コマンドを実行すると、複製されたすべてのls
がメモリ内の履歴から削除され、最後の1つだけが残ります。履歴ファイルに複製されたコマンドを実行すると、メモリ内の履歴は短くなりますが、履歴ファイル自体は増え続けます。
自分のスクリプトを使用して、必要に応じて履歴ファイルをクリーンアップします。
# remove duplicates while preserving input order
function dedup {
awk '! x[$0]++' $@
}
# removes $HISTIGNORE commands from input
function remove_histignore {
if [ -n "$HISTIGNORE" ]; then
# replace : with |, then * with .*
local IGNORE_PAT=`echo "$HISTIGNORE" | sed s/\:/\|/g | sed s/\*/\.\*/g`
# negated grep removes matches
grep -vx "$IGNORE_PAT" $@
else
cat $@
fi
}
# clean up the history file by remove duplicates and commands matching
# $HISTIGNORE entries
function history_cleanup {
local HISTFILE_SRC=~/.bash_history
local HISTFILE_DST=/tmp/.$USER.bash_history.clean
if [ -f $HISTFILE_SRC ]; then
\cp $HISTFILE_SRC $HISTFILE_SRC.backup
dedup $HISTFILE_SRC | remove_histignore >| $HISTFILE_DST
\mv $HISTFILE_DST $HISTFILE_SRC
chmod go-r $HISTFILE_SRC
history -c
history -r
fi
}
これを行うためのよりエレガントな方法を聞きたいです。
注:HISTTIMEFORMATを使用して履歴のタイムスタンプを有効にすると、スクリプトは機能しません。
Bashは状況を改善することができます
history -a
は、最後のデータだけでなく、メモリ内のどの履歴とも一致しない場合にのみ新しいデータを書き込みます。erasedups
設定が設定されている場合、ファイルが読み取られるときに履歴の重複排除を解除します。シンプルな history -w
新しい端末では、上記のばかげたスクリプトの代わりに履歴ファイルをクリーンアップします。問題は間違いなくhistappend
です。私のシステムでテストおよび確認されました。
私の関連する環境は:
$ set | grep HIST
HISTFILE=/Users/hop/.bash_history
HISTFILESIZE=500
HISTIGNORE=' *:&:?:??'
HISTSIZE=500
$ export HISTCONTROL=erasedups
$ shopt | grep hist
cmdhist on
histappend off
histreedit off
histverify off
lithist off
今考えてみると、問題はおそらくhistory -a
にあります。 history -w
は、重複することなく現在の履歴を書き込む必要があるため、並行性の問題を気にしない場合はそれを使用してください。
export HISTCONTROL=ignoreboth
これが私が使っているものです。
[vanuganti@ ~]$ grep HIST .alias*
.alias:HISTCONTROL="erasedups"
.alias:HISTSIZE=20000
.alias:HISTIGNORE=ls:ll:"ls -altr":"ls -alt":la:l:pwd:exit:mc:su:df:clear:ps:h:history:"ls -al"
.alias:export HISTCONTROL HISTSIZE HISTIGNORE
[vanuganti@ ~]$
と作業
[vanuganti@ ~]$ pwd
/Users/XXX
[vanuganti@ ~]$ pwd
/Users/XXX
[vanuganti@ ~]$ history | grep pwd | wc -l
1
.bash_profile内に追加
alias hist="history -a && hist.py"
次に、これをhist.pyとしてパスに配置し、実行可能にします
#!/usr/bin/env python
f = open('/Users/joe/.bash_history')
l = f.readlines()
l.reverse()
short = []
for s in l:
if s.rstrip() not in short:
short.append(s.rstrip())
short.reverse()
for s in short:
print s
短いリストが必要なときは、単にhistと入力してください