web-dev-qa-db-fra.com

Comment puis-je supprimer les doublons dans mon .bash_history, en préservant l'ordre?

J'aime vraiment utiliser control+r pour rechercher récursivement mon historique de commandes. J'ai trouvé quelques bonnes options que j'aime utiliser avec:

# 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

Le seul problème pour moi est que erasedups efface uniquement les doublons séquentiels - de sorte qu'avec cette chaîne de commandes:

ls
cd ~
ls

La commande ls sera en fait enregistrée deux fois. J'ai pensé à exécuter périodiquement w/cron:

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

Cela permettrait de supprimer les doublons, mais malheureusement l'ordre ne serait pas conservé. Si je ne commence pas par sort, je ne pense pas que uniq puisse fonctionner correctement.

Comment supprimer les doublons dans mon .bash_history, en préservant l'ordre?

Crédit supplémentaire:

Y a-t-il des problèmes d'écrasement du .bash_history fichier via un script? Par exemple, si vous supprimez un fichier journal Apache, je pense que vous devez envoyer un signal Nohup/reset avec kill pour qu'il purge sa connexion au fichier. Si tel est le cas avec le .bash_history fichier, je pourrais peut-être utiliser ps pour vérifier et m'assurer qu'il n'y a pas de sessions connectées avant l'exécution du script de filtrage?

69
cwd

Tri de l'histoire

Cette commande fonctionne comme sort|uniq, mais garde les lignes en place

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

Fondamentalement, ajoute à chaque ligne son numéro. Après sort|uniq- ing, toutes les lignes sont triées selon leur ordre d'origine (à l'aide du champ numéro de ligne) et le champ numéro de ligne est supprimé des lignes.

Cette solution a le défaut qu'il n'est pas défini quel représentant d'une classe de lignes égales le fera dans la sortie et donc sa position dans la sortie finale n'est pas définie. Cependant, si le dernier représentant doit être choisi, vous pouvez sort l'entrée par une deuxième clé:

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

Gérer .bash_history

Pour relire et réécrire l'historique, vous pouvez utiliser history -a et history -w respectivement.

37
artistoex

Je cherchais donc exactement la même chose après avoir été ennuyé par les doublons, et j'ai constaté que si je modifiais mon ~/.bash_profile (Mac) avec:

export HISTCONTROL=ignoreboth:erasedups

Il fait exactement ce que vous vouliez, il ne conserve que la dernière de toutes les commandes. ignoreboth, c'est comme faire ignorespace:ignoredups et cela avec erasedups fait le travail.

Au moins sur mon terminal Mac avec bash ce travail est parfait. Je l'ai trouvé ici sur askubuntu.com .

57
sprite

Trouvé cette solution dans la nature et testé:

awk '!x[$0]++'

La première fois qu'une valeur spécifique d'une ligne ($ 0) est vue, la valeur de x [$ 0] est nulle.
La valeur de zéro est inversée avec ! et devient un.
Une instruction qui évalue à un provoque l'action par défaut, qui est imprimer.

Par conséquent, la première fois qu'un $0 est vu, il est imprimé.

La prochaine fois (les répétitions), la valeur de x[$0] a été augmenté,
sa valeur négative est zéro et une instruction qui s'évalue à zéro ne s'imprime pas.

Pour conserver la dernière valeur répétée, inversez l'historique et utilisez le même awk:

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

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

Extension de la réponse de Clayton:

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

tac inversez le fichier, assurez-vous d'avoir installé moreutils pour avoir sponge disponible, sinon utilisez un fichier temporaire.

12
Ali Shakiba

Il s'agit d'une ancienne publication, mais d'un problème perpétuel pour les utilisateurs qui souhaitent avoir plusieurs terminaux ouverts et dont l'historique est synchronisé entre les fenêtres, mais pas dupliqué.

Ma solution en .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
  • l'option histappend ajoute l'historique du tampon à la fin du fichier historique ($ HISTFILE)
  • ignoreboth et erasedups empêchent les entrées en double d'être enregistrées dans le $ HISTFILE
  • La commande Prompt met à jour le cache d'historique
    • history -n lit toutes les lignes de $ HISTFILE qui peuvent s'être produites dans un terminal différent depuis le dernier retour chariot
    • history -w écrit le tampon mis à jour dans $ HISTFILE
    • history -c efface le tampon pour éviter toute duplication
    • history -r relit le $ HISTFILE, en l'ajoutant au tampon désormais vide
  • le script awk stocke la première occurrence de chaque ligne rencontrée. tac l'inverse, puis l'inverse pour qu'il puisse être enregistré avec les commandes les plus récentes encore les plus récentes de l'historique
  • rm le fichier/tmp

Chaque fois que vous ouvrez un nouveau Shell, l'histoire a toutes les dupes effacées, et chaque fois que vous appuyez sur le Enter dans une autre fenêtre Shell/terminal, il met à jour cet historique à partir du fichier.

7
smilingfrog

Ceux-ci conserveraient les dernières lignes dupliquées:

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

L'enregistrement unique de chaque nouvelle commande est délicat. Vous devez d'abord ajouter à ~/.profile Ou similaire:

HISTCONTROL=erasedups
Prompt_COMMAND='history -w'

Ensuite, vous devez ajouter à ~/.bash_logout:

history -a
history -w
0
Steven Penny