web-dev-qa-db-fra.com

Historique de Bash: "ignoredups" et "erasedups" créant un conflit avec l'historique commun à travers les sessions

Tout d'abord, ce n'est pas un doublon de threads existants sur SE. J'ai lu ces deux discussions ( 1st , 2nd ) sur un meilleur historique de bash, mais aucune des réponses ne fonctionne - - Je suis d'ailleurs sur Fedora 15.

J'ai ajouté ce qui suit au .bashrc fichier dans le répertoire utilisateur (/ home/aahan /), et cela ne fonctionne pas. Quelqu'un a une idée?

HISTCONTROL=ignoredups:erasedups  # no duplicate entries
HISTSIZE=1000                     # custom history size
HISTFILESIZE=100000                 # custom history file size
shopt -s histappend                      # append to history, don't overwrite it
Prompt_COMMAND="history -a; history -c; history -r; $Prompt_COMMAND"  # Save and reload the history after each command finishes

D'accord, c'est ce que je veux avec l'historique bash (priorité):

  • ne stockez pas les doublons, supprimez ceux qui existent déjà
  • partager immédiatement l'histoire avec tous les terminaux ouverts
  • toujours ajouter l'historique, pas l'écraser
  • stocker les commandes multi-lignes comme une seule commande (qui est désactivée par défaut)
  • quelle est la taille d'historique et la taille de fichier d'historique par défaut?
92
its_me

C'est en fait un comportement vraiment intéressant et j'avoue que j'ai sous-estimé la question au début. Mais d'abord les faits:

1. Ce qui fonctionne

La fonctionnalité peut être obtenue de plusieurs manières, bien que chacune fonctionne un peu différemment. Notez que, dans chaque cas, pour que l'historique soit "transféré" vers un autre terminal (mis à jour), il faut appuyer sur Enter dans le terminal, où il/elle souhaite récupérer l'historique.

  • option 1:

    shopt -s histappend
    HISTCONTROL=ignoredups
    Prompt_COMMAND="history -a; history -n; $Prompt_COMMAND"
    

    Cela présente deux inconvénients:

    1. Lors de la connexion (ouverture d'un terminal), la dernière commande du fichier d'historique est lue deux fois dans le tampon d'historique du terminal actuel;
    2. Les tampons des différents terminaux ne restent pas synchronisés avec le fichier historique.
  • option 2:

    HISTCONTROL=ignoredups
    Prompt_COMMAND="history -a; history -c; history -r; $Prompt_COMMAND"
    

    (Oui, pas besoin de shopt -s histappend et oui, cela doit être history -c au milieu de Prompt_COMMAND) Cette version présente également deux inconvénients importants:

    1. Le fichier historique doit être initialisé. Il doit contenir au moins une ligne non vide (peut être n'importe quoi).
    2. La commande history peut donner une fausse sortie - voir ci-dessous.

[Modifier] "Et le gagnant est ..."

  • option 3:

    HISTCONTROL=ignoredups:erasedups
    shopt -s histappend
    Prompt_COMMAND="history -n; history -w; history -c; history -r; $Prompt_COMMAND"
    

    C'est aussi loin que possible. Il s'agit de l'option uniquement pour que erasedups et l'historique commun fonctionnent simultanément. This est probablement la solution finale à tous vos problèmes, Aahan.


2. Pourquoi l'option 2 ne semble pas fonctionner (ou: ce qui est vraiment ne fonctionne pas comme prévu)?

Comme je l'ai mentionné, chacune des solutions ci-dessus fonctionne différemment. Mais l'interprétation la plus trompeuse du fonctionnement des paramètres vient de l'analyse de la sortie de la commande history. Dans de nombreux cas, la commande peut donner une sortie false. Pourquoi? Parce que il est exécuté avant la séquence des autres commandes history contenues dans le Prompt_COMMAND! Cependant, lorsque vous utilisez la deuxième ou la troisième option, on peut surveiller les changements de .bash_history contenu (en utilisant watch -n1 "tail -n20 .bash_history" par exemple) et voyez quelle est la véritable histoire.

3. Pourquoi l'option 3 est si compliquée?

Tout dépend de la façon dont erasedups fonctionne. Comme l'indique le manuel bash, "(...) erasedups entraîne la suppression de toutes les lignes précédentes correspondant à la ligne actuelle de la liste d'historique avant l'enregistrement de cette ligne" . Donc, c'est vraiment ce que l'OP voulait (et pas seulement, comme je le pensais précédemment, qu'aucun doublon n'apparaisse en séquence) . Voici pourquoi chacun des history -. les commandes doivent ou ne peuvent pas être dans le Prompt_COMMAND:

  • history -na pour être là avant history -w pour lire à partir de .bash_history les commandes enregistrées depuis tout autre terminal,

  • history -whas pour être là afin de sauvegarder l'historique dans un fichier et effacer les doublons,

  • history -ane doit pas y être placé au lieu de history -w, car il ne déclenche pas l'effacement des doublons,

  • history -c est également nécessaire car il empêche la corbeille du tampon d'historique après chaque commande,

  • et enfin, history -r est nécessaire pour restaurer le tampon d'historique à partir du fichier, rendant ainsi finalement l'historique partagé entre les sessions de terminal.

126
rozcietrzewiacz

Dans votre commande d'invite, vous utilisez le -c commutateur. De man bash:

- c Effacer la liste d'historique en supprimant toutes les entrées

Pour partager votre historique avec tous les terminaux ouverts, vous pouvez utiliser -n:

- n Lire les lignes d'historique non lues depuis le fichier d'historique dans la liste d'historique actuelle. Ce sont des lignes ajoutées au fichier d'historique depuis le début de la session bash en cours.

La taille par défaut est également dans le manuel:

HISTSIZE Le nombre de commandes à retenir dans l'historique des commandes (voir HISTORIQUE ci-dessous). La valeur par défaut est 500.

Pour enregistrer des commandes multilignes:

L'option cmdhist Shell, si elle est activée, oblige le Shell à tenter d'enregistrer chaque ligne d'une commande multiligne dans la même entrée d'historique, en ajoutant des points-virgules si nécessaire pour préserver l'exactitude syntaxique. L'option lithist Shell oblige le Shell à enregistrer la commande avec des sauts de ligne intégrés au lieu de points-virgules.

De plus, vous ne devez pas faire précéder les commandes HIST * de export - ce sont des variables uniquement bash et non des variables d'environnement: HISTCONTROL=ignoredups:erasedups est suffisant.

8
jasonwryan

C'est ce que j'ai trouvé et j'en suis satisfait jusqu'à présent…

alias hfix='history -n && history | sort -k2 -k1nr | uniq -f1 | sort -n | cut -c8- > ~/.tmp$$ && history -c && history -r ~/.tmp$$ && history -w && rm ~/.tmp$$'  
HISTCONTROL=ignorespace  
shopt -s histappend  
shopt -s extglob  
HISTSIZE=1000  
HISTFILESIZE=2000  
export HISTIGNORE="!(+(*\ *))"  
Prompt_COMMAND="hfix; $Prompt_COMMAND" 

REMARQUES:

  • Oui, c'est compliqué ... mais, il supprime tous les doublons tout en préservant la chronologie au sein de chaque terminal!
  • Mon HISTIGNORE ignore toutes les commandes qui n'ont pas d'arguments. Cela peut ne pas être souhaitable pour certaines personnes et peut être laissé de côté.
8
user41176

Utilisez-le à la place:

HISTCONTROL=ignoreboth
1
verndog

Cela ne fonctionne pas, car vous oubliez:

 -n   read all history lines not already read from the history file
      and append them to the history list

Mais il semble history -n est juste bogué quand export HISTCONTROL=ignoreboth:erasedups est en vigueur.

Permet d'expérimenter:

$ Prompt_COMMAND=
$ export HISTCONTROL=ignoreboth:erasedups
$ export HISTFILE=~/.bash_myhistory
$ HISTIGNORE='history:history -w'
$ history -c
$ history -w

Ici, nous activons l'effacement des doublons, basculons l'historique vers un fichier personnalisé, effaçons l'historique. Une fois toutes les commandes terminées, nous avons un fichier historique vide et une commande dans l'historique actuel.

$ history
$ cat ~/.bash_myhistory
$ history
$ 1  [2019-06-17 14:57:19] cat ~/.bash_myhistory

Ouvrez le deuxième terminal et exécutez également ces six commandes. Après ça:

$ echo "X"
$ echo "Y"
$ history -w
$ history
  1  [2019-06-17 15:00:21] echo "X"
  2  [2019-06-17 15:00:23] echo "Y"

Maintenant, votre historique actuel a deux commandes et le fichier historique a:

#1560772821
echo "X"
#1560772823
echo "Y"

Retour au premier terminal:

$ history -n
$ history
1  [2019-06-17 14:57:19] cat ~/.bash_myhistory 
2  [2019-06-17 15:03:12] history -n

Huh ... aucune des commandes echo n'est lue. Basculez à nouveau vers le deuxième terminal et:

$ echo "Z"
$ history -w

Maintenant, le fichier historique est:

#1560772821
echo "X"
#1560772823
echo "Y"
#1560773057
echo "Z"

Passez à nouveau au premier terminal:

$ history -n
$ history
  1  [2019-06-17 14:57:19] cat ~/.bash_myhistory 
  2  [2019-06-17 15:03:12] history -n
echo "Z"

Tu peux voir ça echo "Z" la commande est fusionnée en history -n.

Un autre bug est dû au fait que les commandes sont lues dans l'historique par numéro de commande et non par heure de commande, je pense. Je m'attends à ce que d'autres commandes echo apparaissent dans l'historique

0
Eugen Konkov

Je suis allé avec une combinaison de réponses ici:

  • Je veux que mon historique soit fusionné à la fin, j'ai donc utilisé la fonction trap.
  • Je veux également que mes fichiers d'historique soient séparés par des hôtes, j'ai donc changé HISTFILE pour pointer vers un répertoire et des fichiers nommés en fonction du nom d'hôte.
  • J'ai ajouté l'option ignorespace pour pouvoir taper des mots de passe en texte clair et ne pas les faire apparaître dans l'historique, par exemple: $ ./.secretfunction -user myuser -password mypassword ne sera pas enregistré car il commence par l'espace.
  • J'ai ajouté history* et exit à HISTIGNORE juste pour conserver ces commandes, y compris tout ce qui a été passé comme history | grep awesomecommand sortir de là car je ne le voudrais pas normalement. Vous pouvez en ajouter d'autres comme: déconnexion, fg, bg, etc.
  • J'ai ensuite mis tout cela dans mon fichier ~/.bash_aliases afin qu'il n'y ait pas de conflit sur les mises à niveau comme cela s'est parfois produit.
HISTCONTROL=ignoreboth:erasedups:ignorespace
HISTIGNORE="history*:exit"
HISTFILE=~/.bash_history/.$(hostname)_history
shopt -s histappend
function historymerge {
    history -n; history -w; history -c; history -r;
}
trap historymerge EXIT
Prompt_COMMAND="history -a; $Prompt_COMMAND"

Grep votre ~/pour Prompt_COMMAND pour vous assurer que vous ne l'avez pas déjà modifié avec history -a. J'ai également ajouté le format TIME/DATE HISTTIMEFORMAT="%x %r %Z " J'ai aimé mais je n'ai pas inclus dans le bloc ci-dessus car c'est sensible aux paramètres régionaux.

0
Marlon

erasedups ne supprime pas (comme chomp dans certaines langues) les espaces de début et de fin. Ceci est un bug. Les suppressions ne suppriment pas non plus toutes les entrées précédentes.

0

L'utilisation d'une combinaison de l'option 3 de @ rozcietrzewiacz avec un piège de sortie permettra aux terminaux de conserver leurs propres sessions d'historique indépendantes qui convergent à la fermeture. Il semble même fonctionner correctement avec plusieurs sessions sur différentes machines partageant un répertoire de base distant.

export HISTSIZE=5000
export HISTFILESIZE=5000
export HISTCONTROL=ignorespace:erasedups
shopt -s histappend
function historymerge {
    history -n; history -w; history -c; history -r;
}
trap historymerge EXIT
Prompt_COMMAND="history -a; $Prompt_COMMAND"
  • La fonction historymerge charge les lignes hors session à partir du fichier historique, les combine avec l'historique des sessions, écrit un nouveau fichier historique avec déduplication (écrasant ainsi les lignes en double précédemment ajoutées) et recharge l'historique.

  • En gardant history -a dans l'invite minimise le risque de perte d'historique, car il met à jour le fichier d'historique sans déduplication à chaque commande.

  • Enfin, le piège déclenche historymerge à la fermeture de la session pour un fichier d'historique propre à jour avec les commandes de la session la plus récemment fermée qui se sont propagées à la fin du fichier (je pense).

Avec cela, chaque session de terminal aura sa propre histoire indépendante à partir du lancement. Je trouve cela plus utile car j'ai tendance à avoir différents terminaux ouverts pour différentes tâches (je veux donc répéter différentes commandes dans chacun). C'est aussi heureusement plus simple que de donner un sens à plusieurs terminaux essayant constamment de resynchroniser leur historique ordonné de manière distribuée (bien que vous puissiez toujours le faire délibérément en utilisant la fonction historymerge avec certaines sessions ou tous).

Notez que vous souhaiterez que vos limites de taille soient suffisamment grandes pour contenir la longueur d'historique souhaitée plus le volume de lignes non dédupliquées qui peuvent être ajoutées par toutes les sessions actives simultanément. 5000 me suffit en grande partie grâce à l'utilisation extensive de HISTIGNORE pour filtrer les commandes de faible valeur contenant du spam.

0
HonoredMule