web-dev-qa-db-fra.com

Conserver l'historique de bash dans plusieurs fenêtres de terminal

J'ai toujours plus d'un terminal ouvert. De deux à dix, faisant divers morceaux et bobs. Supposons maintenant que je redémarre et ouvre un autre ensemble de terminaux. Certains se souviennent de certaines choses, certains oublient.

Je veux une histoire qui:

  • Se souvient de tout depuis chaque terminal
  • Est instantanément accessible à partir de chaque terminal (par exemple, si je ls dans un, je passe à un autre terminal déjà en cours d'exécution, puis je presse, ls apparaît)
  • N'oublie pas la commande s'il y a des espaces à l'avant de la commande.

Est-ce que je peux faire pour que bash fonctionne plus comme ça?

565
Oli

Ajoutez ce qui suit à ~/.bashrc

# Avoid duplicates
HISTCONTROL=ignoredups:erasedups  
# When the Shell exits, append to the history file instead of overwriting it
shopt -s histappend

# After each command, append to the history file and reread it
Prompt_COMMAND="${Prompt_COMMAND:+$Prompt_COMMAND$'\n'}history -a; history -c; history -r"
355
Pablo R.

Donc, c'est tout mon histoire .bashrc chose:

export HISTCONTROL=ignoredups:erasedups  # no duplicate entries
export HISTSIZE=100000                   # big big history
export HISTFILESIZE=100000               # big big history
shopt -s histappend                      # append to history, don't overwrite it

# Save and reload the history after each command finishes
export Prompt_COMMAND="history -a; history -c; history -r; $Prompt_COMMAND"

Testé avec bash 3.2.17 sur Mac OS X 10.5, bash 4.1.7 sur 10.6.

257
kch

Voici ma tentative de partage d'historique de session Bash. Cela permettra le partage de l'historique entre les sessions bash de manière à ce que le compteur d'historique ne soit pas mélangé et l'expansion de l'historique comme !number Fonctionnera (avec certaines contraintes).

Utilisation de Bash version 4.1.5 sous Ubuntu 10.04 LTS (Lucid Lynx).

HISTSIZE=9000
HISTFILESIZE=$HISTSIZE
HISTCONTROL=ignorespace:ignoredups

_bash_history_sync() {
    builtin history -a         #1
    HISTFILESIZE=$HISTSIZE     #2
    builtin history -c         #3
    builtin history -r         #4
}

history() {                  #5
    _bash_history_sync
    builtin history "$@"
}

Prompt_COMMAND=_bash_history_sync

Explication:

  1. Ajoutez la ligne qui vient d'être entrée au $HISTFILE (La valeur par défaut est .bash_history). Cela fera augmenter $HISTFILE D'une ligne.

  2. Si vous définissez la variable spéciale $HISTFILESIZE Sur une valeur, Bash tronquera $HISTFILE Pour ne pas dépasser $HISTFILESIZE En supprimant les entrées les plus anciennes.

  3. Effacez l'historique de la session en cours. Cela réduira le compteur d'historique du montant de $HISTSIZE.

  4. Lisez le contenu de $HISTFILE Et insérez-le dans l'historique de la session en cours d'exécution. cela augmentera le compteur d'historique du nombre de lignes dans $HISTFILE. Notez que le nombre de lignes de $HISTFILE N'est pas nécessairement $HISTFILESIZE.

  5. La fonction history() remplace l'historique intégré pour s'assurer que l'historique est synchronisé avant son affichage. Ceci est nécessaire pour l'expansion de l'historique en nombre (plus d'informations à ce sujet plus tard).

Plus d'explication:

  • L'étape 1 garantit que la commande de la session en cours d'exécution est écrite dans le fichier d'historique global.

  • L'étape 4 garantit que les commandes des autres sessions sont lues dans l'historique de session en cours.

  • Étant donné que l'étape 4 augmentera le compteur d'historique, nous devons réduire le compteur d'une manière ou d'une autre. Cela se fait à l'étape 3.

  • À l'étape 3, le compteur d'historique est réduit de $HISTSIZE. À l'étape 4, le compteur d'historique est augmenté du nombre de lignes dans $HISTFILE. À l'étape 2, nous nous assurons que le nombre de lignes de $HISTFILE Est exactement $HISTSIZE (Cela signifie que $HISTFILESIZE Doit être le même que $HISTSIZE).

A propos des contraintes de l'expansion historique:

Lorsque vous utilisez l'extension d'historique par numéro, vous devez toujours rechercher le nombre immédiatement avant de l'utiliser. Cela signifie qu'aucun affichage d'invite bash entre la recherche du numéro et son utilisation. Cela signifie généralement pas d'entrée et pas de ctrl + c.

Généralement, une fois que vous avez plus d'une session Bash, il n'y a aucune garantie que l'expansion d'un historique en nombre conserve sa valeur entre deux affichages d'invite Bash. Parce que lorsque Prompt_COMMAND Est exécuté, l'historique de toutes les autres sessions Bash est intégré à l'historique de la session en cours. Si une autre session bash a une nouvelle commande, les numéros d'historique de la session en cours seront différents.

Je trouve cette contrainte raisonnable. De toute façon, je dois chercher le numéro à chaque fois parce que je ne me souviens pas de chiffres historiques arbitraires.

Habituellement, j'utilise l'extension historique par numéro comme ceci

$ history | grep something #note number
$ !number

Je recommande d'utiliser les options Bash suivantes.

## reedit a history substitution line if it failed
shopt -s histreedit
## edit a recalled history line before executing
shopt -s histverify

Bugs étranges:

L'exécution de la commande d'historique dirigée vers n'importe quoi entraînera cette commande à être répertoriée deux fois dans l'historique. Par exemple:

$ history | head
$ history | tail
$ history | grep foo
$ history | true
$ history | false

Tous seront répertoriés deux fois dans l'historique. Je ne sais pas pourquoi.

Idées d'améliorations:

  • Modifiez la fonction _bash_history_sync() pour qu'elle ne s'exécute pas à chaque fois. Par exemple, il ne doit pas s'exécuter après un CTRL+C Sur l'invite. J'utilise souvent CTRL+C Pour supprimer une longue ligne de commande lorsque je décide que je ne veux pas exécuter cette ligne. Parfois, je dois utiliser CTRL+C Pour arrêter un script d'achèvement Bash.

  • Les commandes de la session en cours doivent toujours être les plus récentes de l'historique de la session en cours. Cela aura également pour effet secondaire qu'un numéro d'historique donné conserve sa valeur pour les entrées d'historique de cette session.

123
lesmana

Je ne connais aucun moyen d'utiliser bash. Mais c'est l'une des fonctionnalités les plus populaires de zsh .
Personnellement, je préfère zsh plutôt que bash donc je recommande de l'essayer.

Voici la partie de mon .zshrc qui traite de l'histoire:

SAVEHIST=10000 # Number of entries
HISTSIZE=10000
HISTFILE=~/.zsh/history # File
setopt APPEND_HISTORY # Don't erase history
setopt EXTENDED_HISTORY # Add additional data to history like timestamp
setopt INC_APPEND_HISTORY # Add immediately
setopt HIST_FIND_NO_DUPS # Don't show duplicates in search
setopt HIST_IGNORE_SPACE # Don't preserve spaces. You may want to turn it off
setopt NO_HIST_BEEP # Don't beep
setopt SHARE_HISTORY # Share history between session/terminals
45
Maciej Piechotka

Pour ce faire, vous devrez ajouter deux lignes à votre ~/.bashrc:

shopt -s histappend
Prompt_COMMAND="history -a;history -c;history -r;$Prompt_COMMAND"

De man bash:

Si l'option shell histappend est activée (voir la description de shopt sous Shell BUILTIN COMMANDS ci-dessous), les lignes sont ajoutées au fichier d'historique, sinon le fichier d'historique est écrasé.

17
Chris Down

Vous pouvez modifier votre invite BASH pour exécuter "history -a" et "history -r" que Muerr a suggéré:

savePS1=$PS1

(au cas où vous gâcher quelque chose, ce qui est presque garanti)

PS1=$savePS1`history -a;history -r`

(notez qu'il s'agit de back-ticks; ils exécuteront history -a et history -r à chaque invite. Puisqu'ils ne produisent aucun texte, votre invite sera inchangée.

Une fois que vous avez configuré votre variable PS1 comme vous le souhaitez, définissez-la de manière permanente dans votre fichier ~/.bashrc.

Si vous souhaitez revenir à votre invite d'origine pendant le test, procédez comme suit:

PS1=$savePS1

J'ai fait des tests de base à ce sujet pour m'assurer que cela fonctionne, mais je ne peux pas parler des effets secondaires de l'exécution de history -a;history -r à chaque invite.

11
Schof

Si vous avez besoin d'une solution de synchronisation d'historique bash ou zsh qui résout également le problème ci-dessous, consultez-la sur http://ptspts.blogspot.com/2011/03/how-to-automatically-synchronize-Shell.html

Le problème est le suivant: j'ai deux fenêtres Shell A et B. Dans la fenêtre Shell A, j'exécute sleep 9999, et (sans attendre la fin du sommeil) dans la fenêtre Shell B, je veux pouvoir voir sleep 9999 dans l'histoire de bash.

La raison pour laquelle la plupart des autres solutions ne résoudront pas ce problème est qu'elles écrivent leurs modifications d'historique dans le fichier d'historique à l'aide de Prompt_COMMAND ou PS1, qui s'exécutent tous deux trop tard, uniquement après le sleep 9999 la commande est terminée.

10
pts

Bon, donc finalement cela m'a ennuyé de trouver une solution décente:

# Write history after each command
_bash_history_append() {
    builtin history -a
}
Prompt_COMMAND="_bash_history_append; $Prompt_COMMAND"

Ce que cela fait est une sorte de fusion de ce qui a été dit dans ce fil, sauf que je ne comprends pas pourquoi rechargez-vous l'historique global après chaque commande. Je me soucie très rarement de ce qui se passe dans les autres terminaux, mais je lance toujours une série de commandes, disons dans un terminal:

make
ls -lh target/*.foo
scp target/artifact.foo vm:~/

(Exemple simplifié)

Et dans un autre:

pv ~/test.data | nc vm:5000 >> output
less output
mv output output.backup1

Pas question que je veuille que la commande soit partagée

9
Yarek T

Vous pouvez utiliser history -a pour ajouter l'historique de la session en cours au fichier historique, puis utilisez history -r sur les autres terminaux pour lire le fichier historique.

9
jtimberman

Voici une alternative que j'utilise. C'est lourd mais cela résout le problème mentionné par @axel_c où parfois vous souhaiterez peut-être avoir une instance d'historique distincte dans chaque terminal (une pour make, une pour la surveillance, une pour vim, etc.).

Je garde un fichier historique annexé distinct que je mets constamment à jour. J'ai le mappage suivant sur un raccourci clavier:

history | grep -v history >> ~/master_history.txt

Cela ajoute tout l'historique du terminal actuel à un fichier appelé master_history.txt dans votre répertoire personnel.

J'ai également un raccourci clavier distinct pour rechercher dans le fichier historique principal:

cat /home/toby/master_history.txt | grep -i

J'utilise le chat | grep car il laisse le curseur à la fin pour entrer dans mon expression régulière. Une façon moins laide de le faire serait d'ajouter quelques scripts à votre chemin pour accomplir ces tâches, mais les raccourcis fonctionnent à mes fins. Je vais également périodiquement extraire l'historique des autres hôtes sur lesquels j'ai travaillé et l'ajouter à mon fichier master_history.txt.

Il est toujours agréable de pouvoir rechercher rapidement et trouver cette regex délicate que vous avez utilisée ou cette étrange doublure Perl que vous avez inventée il y a 7 mois.

8
Toby

Je peux offrir un correctif pour ce dernier: assurez-vous que la variable env HISTCONTROL ne spécifie pas "ignorespace" (ou "ignoreboth").

Mais je ressens votre douleur avec plusieurs sessions simultanées. Il n'est tout simplement pas bien géré en bash.

7
jmanning2k

Voici mon amélioration de @ lesmana réponse . La principale différence est que les fenêtres simultanées ne partagent pas l'historique. Cela signifie que vous pouvez continuer à travailler dans vos fenêtres, sans que le contexte des autres fenêtres ne soit chargé dans vos fenêtres actuelles.

Si vous tapez explicitement "historique", OR si vous ouvrez une nouvelle fenêtre, vous obtenez l'historique de toutes les fenêtres précédentes.

De plus, j'utilise cette stratégie pour archiver chaque commande jamais tapée sur ma machine.

# Consistent and forever bash history
HISTSIZE=100000
HISTFILESIZE=$HISTSIZE
HISTCONTROL=ignorespace:ignoredups

_bash_history_sync() {
  builtin history -a         #1
  HISTFILESIZE=$HISTSIZE     #2
}

_bash_history_sync_and_reload() {
  builtin history -a         #1
  HISTFILESIZE=$HISTSIZE     #2
  builtin history -c         #3
  builtin history -r         #4
}

history() {                  #5
  _bash_history_sync_and_reload
  builtin history "$@"
}

export HISTTIMEFORMAT="%y/%m/%d %H:%M:%S   "
Prompt_COMMAND='history 1 >> ${HOME}/.bash_eternal_history'
Prompt_COMMAND=_bash_history_sync;$Prompt_COMMAND
7
rouble

J'ai choisi de mettre l'historique dans un fichier par tty, car plusieurs personnes peuvent travailler sur le même serveur - la séparation des commandes de chaque session facilite l'audit.

# Convert /dev/nnn/X or /dev/nnnX to "nnnX"
HISTSUFFIX=`tty | sed 's/\///g;s/^dev//g'`
# History file is now .bash_history_pts0
HISTFILE=".bash_history_$HISTSUFFIX"
HISTTIMEFORMAT="%y-%m-%d %H:%M:%S "
HISTCONTROL=ignoredups:ignorespace
shopt -s histappend
HISTSIZE=1000
HISTFILESIZE=5000

L'histoire ressemble maintenant à:

user@Host:~# test 123
user@Host:~# test 5451
user@Host:~# history
1  15-08-11 10:09:58 test 123
2  15-08-11 10:10:00 test 5451
3  15-08-11 10:10:02 history

Avec les fichiers ressemblant à:

user@Host:~# ls -la .bash*
-rw------- 1 root root  4275 Aug 11 09:42 .bash_history_pts0
-rw------- 1 root root    75 Aug 11 09:49 .bash_history_pts1
-rw-r--r-- 1 root root  3120 Aug 11 10:09 .bashrc
6
Litch

Ici, je soulignerai un problème avec

export Prompt_COMMAND="${Prompt_COMMAND:+$Prompt_COMMAND$'\n'}history -a; history -c; history -r"

et

Prompt_COMMAND="$Prompt_COMMAND;history -a; history -n"

Si vous exécutez source ~/.bashrc, $ Prompt_COMMAND sera comme

"history -a; history -c; history -r history -a; history -c; history -r"

et

"history -a; history -n history -a; history -n"

Cette répétition se produit chaque fois que vous exécutez "source ~/.bashrc". Vous pouvez vérifier Prompt_COMMAND après chaque exécution de "source ~/.bashrc" en exécutant "echo $ Prompt_COMMAND".

Vous pouvez voir que certaines commandes sont apparemment cassées: "history -n history -a". Mais la bonne nouvelle est que cela fonctionne toujours, car d'autres parties forment toujours une séquence de commandes valide (impliquant simplement des frais supplémentaires en raison de l'exécution répétée de certaines commandes. Et pas si propre.)

Personnellement, j'utilise la version simple suivante:

shopt -s histappend
Prompt_COMMAND="history -a; history -c; history -r"

qui a la plupart des fonctionnalités sans problème tel que mentionné ci-dessus.

Un autre point à souligner est: il n'y a vraiment rien de magique . Prompt_COMMAND est juste une variable d'environnement bash simple. Les commandes qu'il contient sont exécutées avant d'obtenir l'invite bash (le signe $). Par exemple, votre Prompt_COMMAND est "echo 123" et vous exécutez "ls" dans votre terminal. L'effet est comme exécuter "ls; echo 123".

$ Prompt_COMMAND="echo 123"

sortie (tout comme l'exécution de 'Prompt_COMMAND = "echo 123"; $ Prompt_COMMAND'):

123

Exécutez ce qui suit:

$ echo 3

production:

3
123

"history -a" est utilisé pour écrire les commandes d'historique en mémoire dans ~/.bash_history

"history -c" est utilisé pour effacer les commandes d'historique en mémoire

"history -r" est utilisé pour lire les commandes d'historique de ~/.bash_history dans la mémoire

Voir l'explication de la commande d'historique ici: http://ss64.com/bash/history.html

PS: Comme d'autres utilisateurs l'ont souligné, l'exportation n'est pas nécessaire. Voir: en utilisant l'exportation dans .bashrc

5
fstang

J'ai écrit un script pour définir un fichier d'historique par session ou tâche en fonction des éléments suivants.

        # write existing history to the old file
        history -a

        # set new historyfile
        export HISTFILE="$1"
        export HISET=$1

        # touch the new file to make sure it exists
        touch $HISTFILE
        # load new history file
        history -r $HISTFILE

Il ne faut pas nécessairement enregistrer toutes les commandes d'historique, mais il enregistre celles qui m'intéressent et il est plus facile de les récupérer, puis de passer par chaque commande. Ma version répertorie également tous les fichiers d'historique et offre la possibilité de les parcourir tous.

Source complète: https://github.com/simotek/scripts-config/blob/master/hiset.sh

3
simotek

Voici une solution qui ne mélange pas les historiques des sessions individuelles!

Fondamentalement, il faut stocker l'historique de chaque session séparément et le recréer à chaque invite. Oui, il utilise plus de ressources, mais ce n'est pas aussi lent que cela puisse paraître - le retard ne commence à être perceptible que si vous avez plus de 100 000 entrées d'historique.

Voici la logique de base:

# on every Prompt, save new history to dedicated file and recreate full history
# by reading all files, always keeping history from current session on top.
update_history () {
  history -a ${HISTFILE}.$$
  history -c
  history -r
  for f in `ls ${HISTFILE}.[0-9]* | grep -v "${HISTFILE}.$$\$"`; do
    history -r $f
  done
  history -r "${HISTFILE}.$$"
}
export Prompt_COMMAND='update_history'

# merge session history into main history file on bash exit
merge_session_history () {
  cat ${HISTFILE}.$$ >> $HISTFILE
  rm ${HISTFILE}.$$
}
trap merge_session_history EXIT

Voir cet Gist pour une solution complète, y compris quelques sauvegardes et optimisations de performances.

3
Jan Warchoł

Parce que je préfère l'histoire infinie qui a été enregistrée dans un fichier personnalisé. Je crée cette configuration basée sur https://stackoverflow.com/a/19533853/4632019 :

export HISTFILESIZE=
export HISTSIZE=
export HISTTIMEFORMAT="[%F %T] "

export HISTFILE=~/.bash_myhistory
Prompt_COMMAND="history -a; history -r; $Prompt_COMMAND"
2
Eugen Konkov

Cela fonctionne pour ZSH

##############################################################################
# History Configuration for ZSH
##############################################################################
HISTSIZE=10000               #How many lines of history to keep in memory
HISTFILE=~/.zsh_history     #Where to save history to disk
SAVEHIST=10000               #Number of history entries to save to disk
#HISTDUP=erase               #Erase duplicates in the history file
setopt    appendhistory     #Append history to the history file (no overwriting)
setopt    sharehistory      #Share history across terminals
setopt    incappendhistory  #Immediately append to the history file, not just when a term is killed
2
Mulki

Je le voulais depuis longtemps, en particulier la possibilité de récupérer une commande par où elle était exécutée pour être réexécutée dans un nouveau projet (ou trouver un répertoire par une commande). J'ai donc mis cet outil ensemble, qui combine les solutions précédentes pour stocker un historique CLI global avec un outil interactif de grepping appelé percol (mappé sur C ^ R). Il est toujours lisse sur la première machine que j'ai commencé à utiliser, maintenant avec un historique CLI> 2 ans.

Il ne dérange pas l'historique local de la CLI en ce qui concerne les touches fléchées, mais vous permet d'accéder assez facilement à l'historique global (que vous pouvez également mapper à autre chose que C ^ R)

2
Gordon Wells

Voici l'extrait de mon .bashrc et de courtes explications là où c'est nécessaire:

# The following line ensures that history logs screen commands as well
shopt -s histappend

# This line makes the history file to be rewritten and reread at each bash Prompt
PROMPT_COMMAND="$Prompt_COMMAND;history -a; history -n"
# Have lots of history
HISTSIZE=100000         # remember the last 100000 commands
HISTFILESIZE=100000     # start truncating commands after 100000 lines
HISTCONTROL=ignoreboth  # ignoreboth is shorthand for ignorespace and     ignoredups

Le HISTFILESIZE et le HISTSIZE sont des préférences personnelles et vous pouvez les modifier selon vos goûts.

0
Hopping Bunny