.bash_historyの重複を削除するにはどうすればよいですか?


61

私はcontrol+rコマンド履歴を再帰的に検索するために使用することを本当に楽しんでいます。私はそれで使用したいいくつかの良いオプションを見つけました:

# ignore duplicate commands, ignore commands starting with a space
export HISTCONTROL=erasedups:ignorespace

# keep the last 5000 entries
export HISTSIZE=5000

# append to the history instead of overwriting (good for multiple connections)
shopt -s histappend

私にとって唯一の問題は、erasedups連続した重複のみを消去することです-そのため、この一連のコマンドでは:

ls
cd ~
ls

lsコマンドは、実際には2回記録されます。私は定期的にw / cronを実行することを考えました:

cat .bash_history | sort | uniq > temp.txt
mv temp.txt .bash_history

これにより重複を削除できますが、残念ながら順序は保持されません。私がいない場合はsort、ファイルを最初に、私は信じていませんuniq正常に動作することができます。

.bash_historyの重複を削除するにはどうすればよいですか?

追加クレジット:

.bash_historyスクリプトを使用してファイルを上書きする際に問題はありますか?たとえば、Apacheログファイルを削除する場合はkill、ファイルへの接続をフラッシュするためにnohup / reset信号を送信する必要があると思います。.bash_historyファイルの場合は、psフィルタリングスクリプトを実行する前に、何らかの方法で接続されたセッションがないことを確認して確認できますか?


3
お試しignoredupsの代わりに、erasedupsしばらくの間、それがあなたのためにどのように動作するかを参照してください。
jw013

1
私はbashが履歴ファイルに開いているファイルハンドルを保持しているとは思わない-それがする必要があるときに/がそれを読み書きので、(注-必要があるはず -私がテストしていないが)他の場所からそれを上書きする安全です。
D_Bye

1
私はあなたの質問の最初の文で何か新しいことを学びました。いいトリックだ!
リカルド

historyコマンドのすべてのオプションのmanページを見つけることができません。どこを見ればいいの?
ジョナサンハートリー

履歴オプションは「man bash」にあり、「shell builtin commands」セクションを検索してから、その下の「history」を検索します。
ジョナサンハートリー

回答:


36

履歴の並べ替え

このコマンドはのようsort|uniqに機能しますが、行を所定の位置に保ちます

nl|sort -k 2|uniq -f 1|sort -n|cut -f 2

基本的に、各行にその番号を付加します。sort|uniq-ingの後、すべての行は元の順序に従って(行番号フィールドを使用して)ソートされ、行番号フィールドは行から削除されます。

このソリューションには、出力で同等の行のクラスを表すものが未定義であるため、最終出力での位置が未定義であるという欠陥があります。ただし、最新の担当者を選択する必要がある場合はsort、2番目のキーで入力できます。

nl|sort -k2 -k 1,1nr|uniq -f1|sort -n|cut -f2

.bash_historyの管理

履歴の再読み込みと書き戻しにはhistory -ahistory -wそれぞれとを使用できます。


6
シェルツールで実装されたdecorate-sort-undecorateのバージョン。いいね
ire_and_curses

を使用するsortと、-rスイッチは常にソート順を逆にします。しかし、これはあなたが念頭に置いている結果をもたらしません。sortの2つのオカレンスはls、結果が同じであると見なされます。逆の場合でも、最終的な順序はソートアルゴリズムに依存します。しかし、別のアイデアについては私の更新を参照してください。
artistoex

1
.bash_historyを変更したくない場合は、次を.bashrcに入れることができます。alias history = 'history | sort -k2 -k 1,1nr | uniq -f 1 | sort -n '
ネイサン14年

nl各コード行の先頭には何がありますか?そうではありませんhistoryか?
-AL

1
@AL nlは行番号を追加します。このコマンドは全体として、順序を維持しながら重複を削除するという一般的な問題を解決します。入力は標準入力から読み込まれます。
artistoex

49

そのため、重複に悩まされた後、同じものを探していましたが、〜/ .bash_profile(Mac)を次のように編集すると、

export HISTCONTROL=ignoreboth:erasedups

それはまさにあなたが望んだことをします、それはどんなコマンドの最新のものでも保持します。ignoreboth実際にやっているようなものでignorespace:ignoredups、それerasedupsが仕事を成し遂げます。

少なくともbashを搭載したMac端末では、これは完璧に機能します。ここaskubuntu.comで見つけました。


10
これが正しい答えでなければなりません
MitchBroadhead

Max OS X YosemiteおよびUbuntu 14_04でテスト
リカルド

1
@MitchBroadheadに同意します。これにより、外部のcronジョブを使用せずに、bash自体の問題が解決されます。ubuntu 17.04および16.04 LTSでテスト
ゲオルグユング

OpenBSDでも動作します。履歴ファイルに追加するコマンドの重複を削除するだけで、私にとっては問題ありません。以前に複製として存在していたコマンドを入力すると、履歴ファイルが短くなるという興味深い効果があります。これで、履歴ファイルの最大長を短くすることができます。
WeakPointer

1
これは、重複した連続したコマンドのみを無視します。あなたが与えられた二つのコマンド間を繰り返し交互場合は、あなたのbashの履歴が重複でいっぱいになります
Dylanthepiguy

16

このソリューションを実際に見つけてテストしました。

awk '!x[$0]++'

線の特定の値($ 0)が初めて見られるとき、x [$ 0]の値はゼロです。
ゼロの値はと反転し!、1になります。
1つに評価されるステートメントは、デフォルトのアクションであるprintを引き起こします。

したがって、特定のもの$0が初めて見られるとき、それは印刷されます。

次回(繰り返し)の値x[$0]が含まれ、
その否定された値はゼロであり、ゼロと評価されるステートメントは出力されません。

最後に繰り返された値を保持するには、履歴を逆にして同じawkを使用します。

awk '!x[$0]++' ~/.bash_history                 # keep the first value repeated.

tac ~/.bash_history | awk '!x[$0]++' | tac     # keep the last.

うわー!うまくいきました。しかし、それは私が推測する最初の出現を除くすべてを削除します。これを実行する前に、Sublime Textを使用して行の順序を逆にしました。ここでもう一度逆にして、すべての重複の最後の発生のみが残された、クリーンな履歴を取得します。ありがとうございました。
trss 14

私の答えをチェックしてください!
アリシャキバ

バジリオンのサブプロセスを起動することなく、きれいで一般的な回答(履歴のユースケースに限定されない);
JepZ

9

クレイトンの答えを拡張する:

tac $HISTFILE | awk '!x[$0]++' | tac | sponge $HISTFILE

tacファイルを反転し、インストール済みであることを確認しmoreutilsてからsponge使用可能にするか、一時ファイルを使用します。


1
Macの場合はbrew install coreutils、を使用gし、BSD組み込みMacコマンドとの混乱を避けるために、すべてのGNU utilsが追加されていることに注意してください(たとえば、gsedはGNUで、sedはBSDです)。を使用しますgtac
-tralston

私は歴史-c、それは歴史を使用して取得する履歴-rが必要
drescherjm

4

これらは最後に複製された行を保持します:

ruby -i -e 'puts readlines.reverse.uniq.reverse' ~/.bash_history
tac ~/.bash_history | awk '!a[$0]++' | tac > t; mv t ~/.bash_history

明確にするために、ここで2つの(素晴らしい)ソリューションを示し、ユーザーはそのうちの1つを実行するだけでよいことを理解していますか?ルビーのものか、バッシュのものか?
ジョナサンハートリー

3

これは古い投稿ですが、複数の端末を開いて、ウィンドウ間で履歴を同期させたいが複製しないようにするユーザーにとっては永続的な問題です。

.bashrcでの私のソリューション:

shopt -s histappend
export HISTCONTROL=ignoreboth:erasedups
export PROMPT_COMMAND="history -n; history -w; history -c; history -r"
tac "$HISTFILE" | awk '!x[$0]++' > /tmp/tmpfile  &&
                tac /tmp/tmpfile > "$HISTFILE"
rm /tmp/tmpfile
  • histappendオプションは、バッファーの履歴を履歴ファイル($ HISTFILE)の最後に追加します
  • ignorebothとerasedupsは、$ HISTFILEに重複したエントリが保存されるのを防ぎます
  • promptコマンドは履歴キャッシュを更新します
    • history -n 最後のキャリッジリターン以降に別の端末で発生した可能性のあるすべての行を$ HISTFILEから読み取ります
    • history -w 更新されたバッファを$ HISTFILEに書き込みます
    • history -c 重複が発生しないようにバッファを消去します
    • history -r $ HISTFILEを再読み込みし、空のバッファに追加します
  • awkスクリプトは、遭遇した各行の最初の出現を保存します。tacそれを元に戻し、それから元に戻して、履歴内で最新のコマンドで保存できるようにします
  • / tmpファイルをrm

新しいシェルを開くたびに、履歴はすべて消去さEnterれ、別のシェル/ターミナルウィンドウでキーを押すたびに、ファイルからこの履歴が更新されます。


コメントの中でこれに対する優れた説明があります
-smilingfrog

「ignorebothとerasedupsが重複の保存を妨げている」場合、ファイルから重複を削除するために「awk」コマンドも実行する必要があるのはなぜですか?「ignoreboth and erasedups」は連続した重複の保存を防ぐだけだからでしょうか?退屈して申し訳ありませんが、私は理解しようとしています。
ジョナサンハートリー

1
erasedupsは、連続した重複のみを消去します。そして、awkコマンドがerasedupesコマンドを複製してそれを不要にしていることは正しいです。
笑顔のカエル

ありがとう、それは私に何が起こっているのかを明確にします。
ジョナサンハートレー

0

すべての新しいコマンドを一意に記録することは注意が必要です。最初に追加~/.profileまたは類似する必要があります :

HISTCONTROL=erasedups
PROMPT_COMMAND='history -w'

次に追加する必要があります~/.bash_logout

history -a
history -w

ログアウト時に、履歴ファイル全体を書き換える前に、履歴ファイルに未書き込みの履歴を追加する必要がある理由を理解してもらえますか?「追加」なしでファイル全体を書き込むことはできませんか?
ジョナサンハートリー
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.