it-swarm.it

Conserva la cronologia bash in più finestre del terminale

Ho costantemente più di un terminale aperto. Ovunque da due a dieci, facendo vari bit e bob. Ora diciamo che riavvio e apro un altro set di terminali. Alcuni ricordano certe cose, altri dimenticano.

Voglio una storia che:

  • Ricorda tutto da ogni terminale
  • È immediatamente accessibile da ogni terminale (ad es. Se I ls in uno, passa a un altro terminale già in esecuzione e quindi premo su, ls viene visualizzato)
  • Non dimentica il comando se ci sono spazi nella parte anteriore del comando.

Qualcosa che posso fare per far funzionare Bash in questo modo?

565
Oli

Aggiungi quanto segue a ~/.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.

Quindi, questo è tutto ciò che riguarda la mia storia .bashrc cosa:

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"

Testato con bash 3.2.17 su Mac OS X 10.5, bash 4.1.7 su 10.6.

257
kch

Ecco il mio tentativo di condivisione della cronologia delle sessioni di Bash. Ciò consentirà la condivisione della cronologia tra le sessioni bash in modo tale che il contatore della cronologia non venga confuso e l'espansione della cronologia come !number Funzionerà (con alcuni vincoli).

Utilizzo di Bash versione 4.1.5 in 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 "[email protected]"
}

Prompt_COMMAND=_bash_history_sync

Spiegazione:

  1. Aggiungi la riga appena inserita a $HISTFILE (L'impostazione predefinita è .bash_history). Ciò farà crescere $HISTFILE Di una riga.

  2. Impostando la variabile speciale $HISTFILESIZE Su un valore, Bash troncerà $HISTFILE In modo che non sia più lungo di $HISTFILESIZE Rimuovendo le voci più vecchie.

  3. Cancella la cronologia della sessione in esecuzione. Ciò ridurrà il contatore della cronologia della quantità di $HISTSIZE.

  4. Leggi il contenuto di $HISTFILE E inseriscilo nella cronologia della sessione corrente. questo aumenterà il contatore della cronologia della quantità di righe in $HISTFILE. Nota che il conteggio delle righe di $HISTFILE Non è necessariamente $HISTFILESIZE.

  5. La funzione history() sovrascrive la cronologia incorporata per assicurarsi che la cronologia sia sincronizzata prima che venga visualizzata. Ciò è necessario per l'espansione della cronologia in base al numero (ne parleremo più avanti).

Più spiegazione:

  • Il passaggio 1 garantisce che il comando della sessione in esecuzione corrente venga scritto nel file della cronologia globale.

  • Il passaggio 4 garantisce che i comandi delle altre sessioni vengano letti nella cronologia della sessione corrente.

  • Poiché il passaggio 4 aumenterà il contatore della cronologia, dobbiamo ridurre il contatore in qualche modo. Questo viene fatto al punto 3.

  • Nel passaggio 3 il contatore della cronologia viene ridotto di $HISTSIZE. Nel passaggio 4 il contatore della cronologia viene aumentato del numero di righe in $HISTFILE. Nel passaggio 2 ci assicuriamo che il conteggio delle righe di $HISTFILE Sia esattamente $HISTSIZE (Ciò significa che $HISTFILESIZE Deve essere uguale a $HISTSIZE).

Informazioni sui vincoli dell'espansione della cronologia:

Quando si utilizza l'espansione della cronologia per numero, è necessario sempre cercare il numero immediatamente prima di utilizzarlo. Ciò significa che non viene visualizzato alcun prompt bash tra la ricerca del numero e l'utilizzo. Questo di solito significa no enter e no ctrl + c.

Generalmente, una volta che hai più di una sessione Bash, non vi è alcuna garanzia che un'espansione della cronologia in base al numero manterrà il suo valore tra due display del prompt Bash. Perché quando Prompt_COMMAND Viene eseguito, la cronologia di tutte le altre sessioni di Bash viene integrata nella cronologia della sessione corrente. Se qualsiasi altra sessione bash ha un nuovo comando, i numeri di cronologia della sessione corrente saranno diversi.

Trovo ragionevole questo vincolo. Devo comunque cercare il numero ogni volta perché non ricordo numeri storici arbitrari.

Di solito uso l'espansione della cronologia per numero come questo

$ history | grep something #note number
$ !number

Consiglio di utilizzare le seguenti opzioni di Bash.

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

Strani bug:

L'esecuzione del comando di cronologia inoltrato a qualsiasi cosa comporterà che tale comando venga elencato due volte nella cronologia. Per esempio:

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

Tutto sarà elencato nella storia due volte. Non ho idea del perché.

Idee per miglioramenti:

  • Modifica la funzione _bash_history_sync() in modo che non venga eseguita ogni volta. Ad esempio, non dovrebbe essere eseguito dopo un CTRL+C Sul prompt. Uso spesso CTRL+C Per scartare una lunga riga di comando quando decido di non voler eseguire quella riga. A volte devo usare CTRL+C Per interrompere uno script di completamento di Bash.

  • I comandi della sessione corrente dovrebbero essere sempre i più recenti nella cronologia della sessione corrente. Ciò avrà anche l'effetto collaterale che un determinato numero di cronologia mantiene il suo valore per le voci di cronologia di questa sessione.

123
lesmana

Non sono a conoscenza di alcun modo utilizzando bash. Ma è una delle funzionalità più popolari di zsh .
Personalmente preferisco zsh rispetto a bash quindi ti consiglio di provarlo.

Ecco la parte del mio .zshrc che tratta della storia:

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

Per fare ciò, dovrai aggiungere due righe al tuo ~/.bashrc:

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

A partire dal man bash:

Se l'opzione Shell histappend è abilitata (vedere la descrizione di shopt sotto COMANDI INCORPORATI Shell di seguito), le righe vengono aggiunte al file della cronologia, altrimenti il ​​file della cronologia viene sovrascritto.

17
Chris Down

Puoi modificare il tuo BASH Prompt per eseguire "history -a" e "history -r" che Muerr ha suggerito:

savePS1=$PS1

(nel caso in cui sbagli qualcosa, che è quasi garantito)

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

(nota che si tratta di back-tick; eseguiranno la cronologia -a e la cronologia -r su ogni prompt. Poiché non generano alcun testo, il tuo prompt rimarrà invariato.

Dopo aver impostato la variabile PS1 nel modo desiderato, impostala in modo permanente nel file ~/.bashrc.

Se si desidera tornare al prompt originale durante il test, eseguire:

PS1=$savePS1

Ho fatto dei test di base su questo per assicurarmi che funzioni, ma non posso parlare di effetti collaterali derivanti dall'esecuzione di history -a;history -r su ogni prompt.

11
Schof

Se hai bisogno di una soluzione di sincronizzazione della cronologia bash o zsh che risolva anche il problema riportato di seguito, allora consultalo su http://ptspts.blogspot.com/2011/03/how-to-automatically-synchronize-Shell.html

Il problema è il seguente: ho due finestre Shell A e B. Nella finestra Shell A, eseguo sleep 9999 E (senza attendere il completamento del sonno) nella finestra Shell B, voglio essere in grado di vedi sleep 9999 nella storia di bash.

Il motivo per cui la maggior parte delle altre soluzioni qui non risolve questo problema è che stanno scrivendo le loro modifiche alla cronologia nel file della cronologia usando Prompt_COMMAND O PS1, Entrambi i quali vengono eseguiti troppo tardi, solo al termine del comando sleep 9999.

10
pts

Bene, quindi alla fine questo mi ha infastidito nel trovare una soluzione decente:

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

Ciò che fa è una sorta di fusione di ciò che è stato detto in questo thread, tranne per il fatto che non capisco perché ricarichi la storia globale dopo ogni comando. Mi preoccupo molto raramente di ciò che accade in altri terminali, ma eseguo sempre serie di comandi, ad esempio in un terminale:

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

(Esempio semplificato)

E in un altro:

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

Non vorrei che il comando fosse condiviso

9
Yarek T

Puoi usare history -a per aggiungere la cronologia della sessione corrente al file hist, quindi utilizzare history -r sugli altri terminali per leggere il file histfile.

9
jtimberman

Ecco un'alternativa che uso. È ingombrante ma risolve il problema menzionato da @axel_c in cui a volte potresti voler avere un'istanza di cronologia separata in ciascun terminale (uno per make, uno per monitoraggio, uno per vim, ecc.).

Conservo un file cronologico allegato separato che aggiorno costantemente. Ho il seguente mappato su un tasto di scelta rapida:

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

Questo aggiunge tutta la cronologia dal terminale corrente a un file chiamato master_history.txt nella tua home directory.

Ho anche un tasto di scelta rapida separato per cercare nel file della cronologia principale:

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

Uso cat | grep perché lascia il cursore alla fine per entrare nella mia regex. Un modo meno brutto per farlo sarebbe quello di aggiungere un paio di script al tuo percorso per eseguire queste attività, ma i tasti di scelta rapida funzionano per i miei scopi. Inoltre periodicamente estrarrò la cronologia dagli altri host su cui ho lavorato e aggiungerò quella cronologia al mio file master_history.txt.

È sempre bello poter cercare rapidamente e trovare quella regex complicata che hai usato o quella strana one-liner Perl che ti è venuta in mente 7 mesi fa.

8
Toby

Posso offrire una soluzione per quest'ultima: assicurati che la variabile env HISTCONTROL non specifichi "ignorespace" (o "ignoreboth").

Ma sento il tuo dolore con più sessioni simultanee. Semplicemente non è gestito bene in Bash.

7
jmanning2k

Ecco il mio miglioramento per --- lesmana's risposta . La differenza principale è che le finestre simultanee non condividono la cronologia. Ciò significa che puoi continuare a lavorare nelle tue finestre, senza che il contesto di altre finestre venga caricato nelle finestre attuali.

Se si digita esplicitamente "cronologia", OR se si apre una nuova finestra, si ottiene la cronologia da tutte le finestre precedenti.

Inoltre, uso questa strategia per archiviare tutti i comandi mai digitati sulla mia macchina.

# 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 "[email protected]"
}

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

Ho scelto di inserire la cronologia in un file per cento, poiché più persone possono lavorare sullo stesso server: separare i comandi di ciascuna sessione semplifica il controllo.

# 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

La storia ora sembra:

[email protected]:~# test 123
[email protected]:~# test 5451
[email protected]:~# 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

Con i file che sembrano:

[email protected]:~# 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

Qui sottolineo un problema con

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

e

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

Se esegui source ~/.bashrc, $ Prompt_COMMAND sarà simile

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

e

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

Questa ripetizione si verifica ogni volta che si esegue 'source ~/.bashrc'. Puoi controllare Prompt_COMMAND dopo ogni esecuzione di 'source ~/.bashrc' eseguendo 'echo $ Prompt_COMMAND'.

Potresti vedere alcuni comandi apparentemente non funzionanti: "history -n history -a". Ma la buona notizia è che funziona ancora, perché altre parti formano ancora una sequenza di comandi valida (comporta solo un costo aggiuntivo dovuto all'esecuzione ripetitiva di alcuni comandi. E non così pulito.)

Personalmente utilizzo la seguente versione semplice:

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

che ha la maggior parte delle funzionalità mentre non esiste un problema di questo tipo.

Un altro punto da sottolineare è: non c'è davvero nulla di magico . Prompt_COMMAND è solo una semplice variabile d'ambiente bash. I comandi in esso vengono eseguiti prima di ottenere il prompt bash (il segno $). Ad esempio, Prompt_COMMAND è "echo 123" e si esegue "ls" nel terminale. L'effetto è come eseguire "ls; echo 123".

$ Prompt_COMMAND="echo 123"

output (proprio come eseguendo 'Prompt_COMMAND = "echo 123"; $ Prompt_COMMAND'):

123

Eseguire quanto segue:

$ echo 3

produzione:

3
123

"history -a" è usato per scrivere i comandi di cronologia in memoria su ~/.bash_history

"history -c" è usato per cancellare i comandi di cronologia in memoria

"history -r" è usato per leggere i comandi di cronologia da ~/.bash_history alla memoria

Vedi la spiegazione dei comandi della cronologia qui: http://ss64.com/bash/history.html

PS: Come altri utenti hanno sottolineato, l'esportazione non è necessaria. Vedi: sando export in .bashrc

5
fstang

Ho scritto uno script per impostare un file di cronologia per sessione o attività basato su quanto segue.

        # 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

Non è necessario salvare tutti i comandi della cronologia, ma salva quelli a cui tengo ed è più facile recuperarli e passare attraverso ogni comando. La mia versione elenca anche tutti i file della cronologia e offre la possibilità di cercarli tutti.

Fonte completa: https://github.com/simotek/scripts-config/blob/master/hiset.sh

3
simotek

Ecco una soluzione che non confonde le storie delle singole sessioni!

Fondamentalmente si deve archiviare la cronologia di ogni sessione separatamente e ricrearla su ogni Prompt. Sì, utilizza più risorse, ma non è così lento come potrebbe sembrare - il ritardo inizia a essere evidente solo se si hanno più di 100000 voci di cronologia.

Ecco la logica principale:

# 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

Vedere this Gist per una soluzione completa, incluse alcune garanzie e ottimizzazioni delle prestazioni.

3
Jan Warchoł

Perché preferisco la storia infinita che è stata salvata nel file personalizzato. Creo questa configurazione basata su 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

Questo funziona per 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

Lo desideravo da molto tempo, in particolare la possibilità di recuperare un comando dal punto in cui era stato eseguito per rieseguirlo in un nuovo progetto (o trovare una directory tramite un comando). Quindi ho messo insieme questo strumento , che combina le soluzioni precedenti per la memorizzazione di una cronologia CLI globale con uno strumento di grepping interattivo chiamato percol (mappato su C ^ R). È ancora perfetto sulla prima macchina che ho iniziato a usarlo, ora con una cronologia della CLI di 2 anni.

Non interferisce con la cronologia CLI locale per quanto riguarda i tasti freccia, ma consente di accedere facilmente alla cronologia globale (che è anche possibile mappare a qualcosa di diverso da C ^ R)

2
Gordon Wells

Ecco lo snippet del mio .bashrc e brevi spiegazioni dove necessario:

# 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

HISTFILESIZE e HISTSIZE sono preferenze personali e puoi modificarle secondo i tuoi gusti.

0
Hopping Bunny