Historique BASH tronqué à 500 lignes à chaque connexion

22

Pour une raison quelconque, je ne parviens pas à faire en sorte que mon système conserve mon historique BASH après un redémarrage. Voici les sections pertinentes de mon ~/.bashrc:

shopt -s histappend
PROMPT_COMMAND='history -a; updateWindowTitle'
export HISTCONTROL=ignoredups
export HISTSIZE=9999
export HISTFILESIZE=999999
export HISTFILE="$HOME/.bash_history"

Pour autant que je sache, ce sont toutes les options nécessaires (je sais que je pouvais garder l'historique à travers plusieurs redémarrages sans tout cela dans le passé). Cependant, malgré avoir ajouté ces options il y a plusieurs redémarrages, je perds encore la plupart de mon historique après un redémarrage. Il n'est pas vide, mais il n'a pas les 9999 lignes que j'avais avant de redémarrer.

Avant que quiconque se plaigne, oui, j'ai lu ces questions. J'ai mis en œuvre certaines de leurs suggestions comme indiqué ci-dessus, les autres étaient soit inutiles, soit non pertinentes:

Au cas où il pourrait y avoir d'autres commandes pertinentes, vous pouvez voir l'intégralité de ma commande ~/.bashrc ici .

Alors, qu'est-ce qui me manque? Pourquoi mon historique n'est-il pas sauvegardé? Si quelqu'un pense qu'un autre fichier peut être pertinent, faites le moi savoir et je le posterai. J'ai vérifié en exécutant grep -i hist \.*mon $HOMEqui a montré que le seul .fichier pertinent contenant la chaîne histou l' HISTétait .bashrc.

J'utilise Linux Mint Debian Edition, GNU bash, version 4.2.36 (1) -release (x86_64-pc-linux-gnu) et mon émulateur de terminal préféré (au cas où cela soit pertinent) l'est terminator.


MISE À JOUR:

Suite à la suggestion de @ mpy dans les commentaires, j'ai changé mon ~/.bashrcpour définir HISTFILE=~/bash_historypar opposition à la valeur par défaut ~/.bash_historyet cela semble résoudre le problème des shells interactifs . Les shells de connexion affichent toujours le même comportement, avec l'historique tronqué aux 500lignes. Cependant, aucune HISTvariable associée n'est définie dans les fichiers concernés:

$ for f in /etc/profile ~/.profile ~/.bash_profile ~/.bash_login; do \
   echo -ne "$f :"; echo `grep HIST $f`; \
done
/etc/profile :
/home/terdon/.profile :grep: /home/terdon/.profile: No such file or directory
/home/terdon/.bash_profile :grep: /home/terdon/.bash_profile: No such file or directory
/home/terdon/.bash_login :grep: /home/terdon/.bash_login: No such file or directory
$ grep -r HIST /etc/profile.d/  <-- returns nothing

Alors, pourquoi est mise HISTSIZEet HISTFILESIZEen ~/.bashrcne suffit pas à moins que je mets explicitement $HISTFILEautre chose que la valeur par défaut ~/.bash_history?

terdon
la source
Êtes-vous propriétaire de .bash_history ou root? Do ls -l .bash_history
Adnan Bhatti
1
@MSStp oui, il m'appartient. Merci pour la suggestion, mais je ne vois pas comment cela pourrait être un problème d'autorisations de toute façon, soit j'y ai accès en lecture / écriture, soit je ne le fais pas. Puisqu'une certaine histoire est sauvée, je le fais clairement. Si bash avait un paramètre qui causait des problèmes lorsque ce fichier n'appartenait pas à l'utilisateur, il se plaindrait ou la fonctionnalité d'historique entière ne fonctionnerait pas.
terdon
lorsque vous exécutez la historycommande, la sortie que vous voyez est identique à ce que vous voyez, en cours d'exécution cat .bash_history, à part les numéros de ligne? Je veux dire que la historycommande répertorie les horodatages ou d'autres informations? La raison pour laquelle je demande, c'est que si vous voyez ces trucs ésotériques, cela signifie qu'il y a un autre module / fonction / programme, qui dérange l'historique du shell et une version incorrecte ou boguée de quoi que ce soit, pourrait vous causer du chagrin .
MelBurslan
@Mel_Burslan oui c'est la même chose, la seule différence est les numéros de ligne.
terdon
2
Ok, une suggestion quelque peu bizarre, mais c'est aussi un problème bizarre ;): Essayez un autre fichier en tant que HISTFILE, pas la valeur par défaut ~/.bash_history. Explication très construite: je suppose que bash est votre shell par défaut, donc au démarrage du système, un shell non interactif est le parent de votre session X (je suppose également que vous utilisez X), qui ne saura rien de l'option histappend (car .bashrc n'est que lu par des shells interactifs), donc tant que ce shell parent s'exécute, tout va bien, mais à la fin (c'est-à-dire l'arrêt du système), il remplacera ~/.bash_history(ce qui est par défaut) et gâchera votre historique ...
mpy

Réponses:

17

Le problème se résume en fait au comportement différent des shells de connexion et de non-connexion. J'avais défini les variables qui contrôlent l'historique dans mon ~/.bahsrc. Ce fichier n'est pas lu lorsque l'on démarre un shell de connexion, il n'est lu que par des shells interactifs non-login (de man bash):

Lorsque bash est invoqué en tant que shell de connexion interactif, ou en tant que shell non interactif avec l' --loginoption, il lit et exécute d'abord les commandes du fichier /etc/profile, si ce fichier existe. Après avoir lu ce fichier, il recherche ~ / .bash_profile,, ~/.bash_loginet ~/.profile, dans cet ordre, et lit et exécute les commandes du premier qui existe et est lisible. L' --noprofileoption peut être utilisée lorsque le shell est démarré pour inhiber ce comportement.

[. . . ]

Lorsqu'un shell interactif qui n'est pas un shell de connexion est démarré, bash lit et exécute des commandes à partir de ~ / .bashrc, si ce fichier existe. Cela peut être inhibé en utilisant l'option --norc. L'option --rcfile file obligera bash à lire et exécuter des commandes à partir du fichier au lieu de ~ / .bashrc.

Par conséquent, chaque fois que je me connectais, que je tombais sur un tty ou .historyque j'utilisais ssh, le fichier était tronqué parce que je ne l'avais pas également défini à la bonne taille ~/.profile. J'ai finalement réalisé cela et j'ai simplement mis les variables à ~/.profile leur place, au lieu de~/.bashrc

Donc, la raison pour laquelle ~/.historyje devais être tronquée était parce que je n'avais défini que les variables HISTORY dans un fichier lu par des shells interactifs sans connexion et donc chaque fois que j'exécutais un type de shell différent, les variables seraient ignorées et le fichier serait coupé en conséquence.

terdon
la source
Très bon point, merci du partage! Cependant, je ne suis pas d'accord avec: ce fichier n'est pas lu lorsque l'on démarre un shell de connexion, il n'est lu que par des shells interactifs. Parce qu'un shell de connexion peut également être interactif. Sinon, tout le mécanisme de l'histoire n'aurait aucun sens. IMHO .bashrcn'est pas lu par (coquilles de connexion OU non interactives).
mpy
@mpy En effet, désolé, je voulais dire des shells interactifs sans connexion. Réponse modifiée.
terdon
1
@terdon, l'idiome commun est que les options du shell interactif ne vont pas dans ~ / .profile ou ~ / .bash_profile. Les options du shell interactif sont placées dans ~ / .bashrc. Pour éviter d'avoir à maintenir les paramètres à deux endroits, les commandes suivantes peuvent être placées en haut d'un ~ / .bash_profile: export BASH_ENV=~/.bashrc ; if [ -f ~/.bashrc ]; then . ~/.bashrc; fi... et en haut du ~ / .bashrc, vérifiez que vous êtes vraiment en cours d'exécution de manière interactive: [ -z "$PS1" ] && return... Bien sûr, c'est juste un idiome.
Noah Spurrier
1
@NoahSpurrier BASH_ENVn'est pas pertinent, il n'affecte que les shells non interactifs. Quant à «l'idiome», c'est quelque chose que Debian a commencé et avec lequel je ne suis personnellement pas d'accord. J'ai souvent des options graphiques ( xsetet similaires) dans mon .bashrc et je ne veux pas que celles-ci soient actives lorsque j'exécute des shells de connexion à partir d'un tty ou via ssh. Je veux que mon .profile et .bashrc soient séparés. De nombreux gestionnaires de connexion (mais pas tous) génèrent un .profile lorsque vous vous connectez, il est donc préférable de définir les variables globales là où elles ne seront lues qu'une fois et non chaque fois que vous ouvrirez un terminal.
terdon
1
@WilsonF oui. Les fichiers sont lus séquentiellement et les fichiers personnels ( ~/.profileou ~/.bashrc) sont lus en dernier. Tout ce qui y est défini aura la priorité sur les paramètres globaux. Vous avez tout à fait raison, vous ne devriez pas définir ces variables /etc/bash.bashrc. Utilisez ~/.profile(ou ~/.bash_profile) à la place.
terdon
11

Ma suggestion est d'utiliser un autre fichier comme HISTFILE, pas la valeur par défaut ~/.bash_history.

Bien que je n'aie aucune explication analytique, je vais essayer de décrire ce qui m'a amené à cette suggestion: si vous utilisez bashcomme shell (de connexion) par défaut et que vous utilisez également X(ce qui est très probable), vous avez une bashinstance en cours d'exécution juste après le (graphique ) s'identifier:

systemd
 ...
  |-login
  |   `-bash      <<====
  |       `-slim
  |           |-X -nolisten tcp vt07 -auth /var/run/slim.auth
  |           |  `-{X}
  |           `-fluxbox
  |               `-xterm -bg black -fg white
  |                   `-bash
 ...

Je pense que cette instance est un shell de connexion, donc elle ne lit pas votre ~/.bashrcet donc ne saura rien sur l' histappendoption:

man bash (1) : Lorsqu'un shell interactif qui n'est pas un shell de connexion est démarré, bash lit et exécute les commandes de /etc/bash.bashrc et ~ / .bashrc, si ces fichiers existent. (...)

Tant que ce "shell parent" s'exécute, tout va bien, mais à sa fin (c'est-à-dire l'arrêt du système), il remplacera ~/.bash_history(car c'est la valeur par défaut) et gâche votre historique ou le clippe au démarrage du système (à nouveau par défaut) 500 lignes. (Ou peut-être les deux ...)

Il me semble également qu'il ne suffit pas d'inclure la configuration de l'historique ~/.bashrc, car cela ne devrait pas être une configuration aussi rare. Je n'ai aucune explication à cela.


Concernant votre problème, que "les shells de connexion affichent toujours le même comportement", vous pouvez essayer d'inclure également la configuration de l'historique dans ~/.bash_profile:

man bash (1) : Lorsque bash est appelé en tant que shell de connexion interactif, ou en tant que shell non interactif avec l'option --login, il lit et exécute d'abord les commandes du fichier / etc / profile, si ce fichier existe. Après avoir lu ce fichier, il recherche ~ / .bash_profile, (...)

Malheureusement, je ne peux pas poster une explication plus justifiée avec des détails de ma propre bashconfiguration, car je suis un zshgars ...

mpy
la source
2
La définition explicite des options d'historique dans mon a ~/.bash_profilerésolu le problème. J'utilise maintenant ~/.bash_historymon fichier d'historique, mais j'ai simplement ajouté toutes les lignes de celles ~/.bashrcindiquées dans ma question à ~/.bash_profile. Vous ne savez toujours pas qui a foutu les coquilles interactives, mais cela semble fonctionner maintenant, merci!
terdon
1
Désolé de ne pas accepter mais j'avais oublié de poster une réponse expliquant ce qui se passait. Je l'ai compris avec l'aide de @Gilles il y a quelque temps et j'ai finalement pu poster une réponse. Je voulais accepter le mien car il explique en fait le problème au lieu de proposer une (très bonne) solution de contournement et pourrait aider les futurs visiteurs.
terdon
2

Étant donné que tous vos paramètres sont en ordre en fonction de la page de manuel et que le fichier historique n'est pas limité par la taille (octets), la seule explication possible à laquelle je peux penser. Cela a à voir avec la façon dont la coquille meurt.

Selon la référence en ligne, la sortie gracieuse (historique enregistré) se produit uniquement lorsque le shell reçoit SIGHUP. Je ne peux pas vraiment expliquer comment votre système propage les signaux lors du redémarrage, mais je soupçonne que votre shell se ferme avec SIGKILL ou SIGPWR.

Cela peut être dû au fait que votre WM s'exécute de manière asynchrone (attendez) et que l'émulateur de terminal généré à partir du WM où bash reçoit un signal de forçage de sortie autre que SIGHUP. Il se peut également que le système d'exploitation envoie rapidement le "dernier kill" à tous les processus avant que le gracieux SIGHUP initial ne parvienne à accéder au shell via X -> WM -> xterm, peut-être parce que X ou WM prend plus de temps à quitter que il faut que l'OS soit prêt à descendre.

Je suis en eaux profondes avec ce genre de choses, mais je pense que quelque chose dans ce sens provoque un comportement erratique. J'ai déjà eu ce problème, et le remède le plus solide est exitdans bash où vous souhaitez conserver l'historique.

J'ai remarqué history -adans votre question, et je ne vois pas pourquoi cela ne suffirait pas à préserver l'histoire.

Vous pouvez résoudre le problème en découvrant ce qui tue vraiment votre bash et en passant à la détermination de l'origine du signal et à la résolution du problème, ou simplement vider l'historique lorsque vous savez quel signal est le dernier (en supposant que les disques sont toujours en ligne d'ici là ):

trap "echo got 1  >/tmp/sig1;  exit" SIGHUP
trap "echo got 2  >/tmp/sig2;  exit" SIGINT
trap "echo got 15 >/tmp/sig15; exit" SIGTERM
 .. and so on...

La capture d'écran incluse illustre ce dont je parle dans les deuxième et troisième paragraphes. La séquence là-bas est que je bombarde de gauche , tue la coquille gauche de droite et cat l'histoire.

homme bash

Au démarrage, (...) Le fichier nommé par la valeur de HISTFILE est tronqué, si nécessaire, pour ne pas contenir plus que le nombre de lignes spécifié par la valeur de HISTFILESIZE (+ 500 par défaut).

Si l'option shell histappend est activée (+ par défaut ici), les lignes sont ajoutées au fichier historique, sinon le fichier historique est écrasé.

référence en ligne

3.7.6 Signaux

Lorsque Bash est interactif, en l'absence de tout piège, il ignore SIGTERM (de sorte que 'kill 0' ne tue pas un shell interactif), et SIGINT est intercepté et géré (de sorte que la fonction d'attente intégrée est interruptible). Lorsque Bash reçoit un SIGINT, il sort de toutes les boucles en cours d'exécution. Dans tous les cas, Bash ignore SIGQUIT. Si le contrôle des travaux est en vigueur (voir Contrôle des travaux), Bash ignore SIGTTIN, SIGTTOU et SIGTSTP.

Les commandes non intégrées lancées par Bash ont des gestionnaires de signaux définis sur les valeurs héritées par le shell de son parent. Lorsque le contrôle des travaux n'est pas en vigueur, les commandes asynchrones ignorent SIGINT et SIGQUIT en plus de ces gestionnaires hérités. Les commandes exécutées à la suite de la substitution de commandes ignorent les signaux de contrôle des travaux générés par le clavier SIGTTIN, SIGTTOU et SIGTSTP.

Le shell se ferme par défaut à réception d'un SIGHUP. Avant de quitter, un shell interactif renvoie le SIGHUP à toutes les tâches, en cours d'exécution ou arrêtées. Les travaux arrêtés sont envoyés SIGCONT pour s'assurer qu'ils reçoivent le SIGHUP. Pour empêcher le shell d'envoyer le signal SIGHUP à un travail particulier, il doit être supprimé de la table des travaux avec le programme intégré désavoué (voir les commandes intégrées de contrôle des travaux) ou marqué pour ne pas recevoir SIGHUP en utilisant disown -h.

Si l'option de shell huponexit a été définie avec shopt (voir The Shopt Builtin), Bash envoie un SIGHUP à toutes les tâches lorsqu'un shell de connexion interactif se ferme.

Si Bash attend la fin d'une commande et reçoit un signal pour lequel un trap a été défini, le trap ne sera pas exécuté tant que la commande ne sera pas terminée. Lorsque Bash attend une commande asynchrone via la fonction d'attente intégrée, la réception d'un signal pour lequel une interruption a été définie entraînera le retour immédiat de la fonction d'attente intégrée avec un état de sortie supérieur à 128, immédiatement après lequel la interruption est exécutée.

capture d'écran démonstrative

signaux

Ярослав Рахматуллин
la source
Je ne comprends pas vraiment ce que vous faites dans cette capture d'écran. Qu'est-ce /tmp/psoque tu tues? Je vois votre point sur les différents signaux de mise à mort (bien que, comme vous le dites, je pensais que c'était history -alà le problème). Je vais tester cela pendant un certain temps et faire rapport.
terdon
$ cat tmp / ps * \ n ps -o pid = | head -n1> ~ / tmp / pso \ n 17201 \ n
Ярослав Рахматуллин
0

Vérifiez / etc / profile et /etc/profile.d/*

Il y a peut-être quelque chose qui dérange les paramètres d'historique.

mabagu
la source
Merci, mais grep -r HIST /etc/profile.d/ne renvoie rien, et j'ai déjà vérifié /etc/profile.
terdon