it-swarm-es.tech

Preserve el historial de bash en múltiples ventanas de terminal

Constantemente tengo más de una terminal abierta. En cualquier lugar de dos a diez, haciendo varios bits y bobs. Ahora digamos que reinicio y abro otro conjunto de terminales. Algunos recuerdan ciertas cosas, algunos olvidan.

Quiero una historia que:

  • Recuerda todo desde cada terminal
  • Es accesible instantáneamente desde cada terminal (por ejemplo, si I ls en una, cambio a otra terminal que ya se está ejecutando y luego presiona hacia arriba, aparece ls)
  • No olvida el comando si hay espacios al frente del comando.

¿Algo que pueda hacer para que bash funcione más así?

565
Oli

Agregue lo siguiente 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.

Entonces, esto es todo lo relacionado con mi historial .bashrc:

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"

Probado con bash 3.2.17 en Mac OS X 10.5, bash 4.1.7 en 10.6.

257
kch

Aquí está mi intento de compartir el historial de sesiones de Bash. Esto permitirá compartir el historial entre sesiones bash de manera que el contador del historial no se mezcle y la expansión del historial como !number Funcionará (con algunas restricciones).

Usando Bash versión 4.1.5 bajo 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

Explicación:

  1. Agregue la línea recién ingresada a $HISTFILE (El valor predeterminado es .bash_history). Esto hará que $HISTFILE Crezca una línea.

  2. Al establecer la variable especial $HISTFILESIZE En algún valor, Bash truncará $HISTFILE Para que no supere las líneas $HISTFILESIZE Al eliminar las entradas más antiguas.

  3. Borrar el historial de la sesión en ejecución. Esto reducirá el contador del historial en la cantidad de $HISTSIZE.

  4. Lea los contenidos de $HISTFILE E insértelos en el historial actual de la sesión en ejecución. esto elevará el contador del historial por la cantidad de líneas en $HISTFILE. Tenga en cuenta que el recuento de líneas de $HISTFILE No es necesariamente $HISTFILESIZE.

  5. La función history() anula el historial incorporado para asegurarse de que el historial esté sincronizado antes de que se muestre. Esto es necesario para la expansión del historial por número (más sobre esto más adelante).

Más explicaciones:

  • El paso 1 asegura que el comando de la sesión actual se escriba en el archivo de historial global.

  • El paso 4 asegura que los comandos de las otras sesiones se lean en el historial de la sesión actual.

  • Debido a que el paso 4 elevará el contador de historial, necesitamos reducir el contador de alguna manera. Esto se hace en el paso 3.

  • En el paso 3, el contador de historial se reduce en $HISTSIZE. En el paso 4, el contador de historial se eleva por el número de líneas en $HISTFILE. En el paso 2, nos aseguramos de que el recuento de líneas de $HISTFILE Sea exactamente $HISTSIZE (Esto significa que $HISTFILESIZE Debe ser el mismo que $HISTSIZE).

Sobre las limitaciones de la expansión de la historia:

Cuando use la expansión del historial por número, debe siempre buscar el número inmediatamente antes de usarlo. Eso significa que no hay una pantalla de solicitud de bash entre buscar el número y usarlo. Eso generalmente significa no enter y no ctrl + c.

En general, una vez que tiene más de una sesión de Bash, no hay garantía alguna de que una expansión del historial por número retenga su valor entre dos pantallas de Bash Prompt. Porque cuando se ejecuta Prompt_COMMAND, El historial de todas las demás sesiones de Bash se integra en el historial de la sesión actual. Si cualquier otra sesión bash tiene un nuevo comando, los números del historial de la sesión actual serán diferentes.

Encuentro esta restricción razonable. De todos modos, tengo que buscar el número cada vez porque no puedo recordar números arbitrarios del historial.

Usualmente uso la expansión del historial por un número como este

$ history | grep something #note number
$ !number

Recomiendo usar las siguientes opciones de Bash.

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

Errores extraños:

Ejecutar el comando de historial canalizado a cualquier cosa hará que ese comando aparezca en el historial dos veces. Por ejemplo:

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

Todos aparecerán en la historia dos veces. No tengo ni idea de porqué.

Ideas para mejoras:

  • Modifique la función _bash_history_sync() para que no se ejecute siempre. Por ejemplo, no debe ejecutarse después de un CTRL+C En el indicador. A menudo uso CTRL+C Para descartar una línea de comando larga cuando decido que no quiero ejecutar esa línea. A veces tengo que usar CTRL+C Para detener un script de finalización de Bash.

  • Los comandos de la sesión actual siempre deben ser los más recientes en el historial de la sesión actual. Esto también tendrá el efecto secundario de que un número de historial dado mantiene su valor para las entradas de historial de esta sesión.

123
lesmana

No conozco ninguna forma de usar bash. Pero es una de las características más populares de zsh .
Personalmente prefiero zsh sobre bash así que recomiendo probarlo.

Aquí está la parte de mi .zshrc que trata de la historia:

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

Para hacer esto, necesitará agregar dos líneas a su ~/.bashrc:

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

Desde man bash:

Si la opción Shell histappend está habilitada (consulte la descripción de shopt en COMANDOS DE CONSTRUIR Shell a continuación), las líneas se agregan al archivo de historial; de lo contrario, el archivo de historial se sobrescribe.

17
Chris Down

Puede editar su mensaje BASH para ejecutar el "historial -a" y el "historial -r" que sugirió Muerr:

savePS1=$PS1

(en caso de que arruines algo, lo cual está casi garantizado)

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

(tenga en cuenta que estos son ticks de retroceso; ejecutarán el historial -a e history -r en cada solicitud. Dado que no generan ningún texto, su solicitud no se modificará.

Una vez que haya configurado su variable PS1 de la manera que desee, configúrela permanentemente en su archivo ~/.bashrc.

Si desea volver a su mensaje original durante la prueba, haga lo siguiente:

PS1=$savePS1

He realizado pruebas básicas sobre esto para asegurarme de que funciona, pero no puedo hablar de ningún efecto secundario al ejecutar history -a;history -r en cada aviso.

11
Schof

Si necesita una solución de sincronización de historial bash o zsh que también resuelva el problema a continuación, puede verla en http://ptspts.blogspot.com/2011/03/how-to-automatically-synchronize-Shell.html

El problema es el siguiente: tengo dos ventanas de Shell A y B. En la ventana de Shell A, ejecuto sleep 9999, y (sin esperar a que termine el sueño) en la ventana B de Shell, quiero poder ver sleep 9999 en la historia de bash.

La razón por la cual la mayoría de las otras soluciones aquí no resolverán este problema es que están escribiendo sus cambios de historial en el archivo de historial usando Prompt_COMMAND o PS1, los cuales se están ejecutando demasiado tarde, solo después de sleep 9999 el comando ha terminado.

10
pts

Bien, así que finalmente esto me molestó encontrar una solución decente:

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

Lo que esto hace es una especie de amalgama de lo que se dijo en este hilo, excepto que no entiendo por qué volverías a cargar la historia global después de cada comando. Muy rara vez me importa lo que sucede en otras terminales, pero siempre ejecuto una serie de comandos, por ejemplo, en una terminal:

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

(Ejemplo simplificado)

Y en otro:

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

De ninguna manera me gustaría que se compartiera el comando

9
Yarek T

Puedes usar history -a para agregar el historial de la sesión actual al archivo histórico, luego use history -r en los otros terminales para leer el archivo histórico.

9
jtimberman

Aquí hay una alternativa que uso. Es engorroso pero aborda el problema que @axel_c mencionó en el que a veces es posible que desee tener una instancia de historial separada en cada terminal (una para hacer, una para monitorear, una para vim, etc.).

Mantengo un archivo de historial adjunto separado que actualizo constantemente. Tengo lo siguiente asignado a una tecla de acceso rápido:

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

Esto agrega todo el historial desde la terminal actual a un archivo llamado master_history.txt en su directorio de inicio.

También tengo una tecla de acceso rápido separada para buscar a través del archivo de historial maestro:

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

Yo uso gato | grep porque deja el cursor al final para ingresar mi expresión regular. Una manera menos fea de hacer esto sería agregar un par de scripts a su ruta para realizar estas tareas, pero las teclas de acceso rápido funcionan para mis propósitos. También periódicamente extraeré el historial de otros hosts en los que he trabajado y agregaré ese historial a mi archivo master_history.txt.

Siempre es agradable poder buscar rápidamente y encontrar esa expresión regular difícil de usar que usaste o esa extraña frase Perl que se te ocurrió hace 7 meses.

8
Toby

Puedo ofrecer una solución para ese último: asegúrese de que la variable env HISTCONTROL no especifique "ignorespace" (o "ignoreboth").

Pero siento tu dolor con múltiples sesiones concurrentes. Simplemente no se maneja bien en bash.

7
jmanning2k

Aquí está mi mejora para @ lesmana's respuesta . La principal diferencia es que las ventanas concurrentes no comparten el historial. Esto significa que puede seguir trabajando en sus ventanas, sin que el contexto de otras ventanas se cargue en sus ventanas actuales.

Si escribe explícitamente 'historial', OR si abre una nueva ventana, obtendrá el historial de todas las ventanas anteriores.

Además, uso esta estrategia para archivar todos los comandos que he escrito en mi máquina.

# 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

Elegí poner el historial en un archivo por tty, ya que varias personas pueden estar trabajando en el mismo servidor; la separación de los comandos de cada sesión facilita la auditoría.

# 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 historia ahora se ve así:

[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 los archivos como:

[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

Aquí señalaré un problema con

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

y

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

Si ejecuta source ~/.bashrc, $ Prompt_COMMAND será como

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

y

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

Esta repetición ocurre cada vez que ejecuta 'source ~/.bashrc'. Puede verificar Prompt_COMMAND después de cada vez que ejecute 'source ~/.bashrc' ejecutando 'echo $ Prompt_COMMAND'.

Podría ver que algunos comandos aparentemente están rotos: "history -n history -a". Pero la buena noticia es que todavía funciona, porque otras partes todavía forman una secuencia de comandos válida (solo implica un costo adicional debido a la ejecución de algunos comandos de forma repetitiva. Y no tan limpio).

Personalmente uso la siguiente versión simple:

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

que tiene la mayoría de las funcionalidades, mientras que no existe el problema mencionado anteriormente.

Otro punto a destacar es: realmente no hay nada mágico . Prompt_COMMAND es solo una variable de entorno de bash simple. Los comandos en él se ejecutan antes de obtener bash Prompt (el signo $). Por ejemplo, su Prompt_COMMAND es "echo 123" y ejecuta "ls" en su terminal. El efecto es como ejecutar "ls; echo 123".

$ Prompt_COMMAND="echo 123"

salida (como ejecutar 'Prompt_COMMAND = "echo 123"; $ Prompt_COMMAND'):

123

Ejecute lo siguiente:

$ echo 3

salida:

3
123

"history -a" se usa para escribir los comandos de historial en memoria en ~/.bash_history

"history -c" se usa para borrar los comandos de historial en la memoria

"history -r" se usa para leer comandos de historial desde ~/.bash_history a la memoria

Consulte la explicación del comando de historial aquí: http://ss64.com/bash/history.html

PD: Como han señalado otros usuarios, exportar es innecesario. Ver: sando exportar en .bashrc

5
fstang

He escrito un script para configurar un archivo de historial por sesión o tarea que se basa en lo siguiente.

        # 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

No es necesario que guarde todos los comandos del historial, pero guarda los que me interesan y es más fácil recuperarlos que pasar por cada comando. Mi versión también enumera todos los archivos del historial y ofrece la posibilidad de buscar en todos ellos.

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

3
simotek

¡Aquí hay una solución que no mezcla historias de sesiones individuales!

Básicamente, uno tiene que almacenar el historial de cada sesión por separado y recrearlo en cada Aviso. Sí, utiliza más recursos, pero no es tan lento como puede parecer: el retraso comienza a ser notable solo si tiene más de 100000 entradas en el historial.

Aquí está la lógica central:

# 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

Consulte este Gist para obtener una solución completa, que incluye algunas medidas de seguridad y optimizaciones de rendimiento.

3
Jan Warchoł

Porque prefiero la historia infinita que guardé en un archivo personalizado. Creo esta configuración basada en 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

Esto funciona para 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

Hace tiempo que quería esto, especialmente la capacidad de recuperar un comando por donde se ejecutó para volver a ejecutarlo en un nuevo proyecto (o encontrar un directorio mediante un comando). Así que puse esta herramienta juntos, que combina soluciones anteriores para almacenar un historial de CLI global con una herramienta interactiva de grepping llamada percol (asignada a C ^ R). Todavía está resbaladizo en la primera máquina que comencé a usarlo, ahora con un historial de CLI> 2 años.

No se mete con el historial de CLI local en lo que respecta a las teclas de flecha, pero le permite acceder al historial global con bastante facilidad (que también puede asignar a algo que no sea C ^ R)

2
Gordon Wells

Aquí está el fragmento de mi .bashrc y explicaciones cortas donde sea necesario:

# 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 y HISTSIZE son preferencias personales y puede cambiarlas según sus gustos.

0
Hopping Bunny