Actualice el historial de bash en otras terminales al salir de una terminal

15

Sé que esta pregunta no es oscura, ya que se hace aquí y se actualiza (y se duplica aquí).

Lo que intento lograr es un poco diferente. No me gusta la idea de que mi mensaje reescriba un archivo cada vez lsque escribo ( history -a; history -c; history -r).

Me gustaría actualizar el archivo al salir. Eso es fácil (en realidad, predeterminado), pero debe agregar en lugar de reescribir:

shopt -s histappend

Ahora, cuando un terminal está cerrado, me gustaría que todos los demás que permanecen abiertos estén al tanto de la actualización.

Prefiero hacer esto sin verificar a través $PS1de todo lo commandque escribo. Creo que sería mejor capturar algún tipo de señal. ¿Cómo lo harías tú? Si no es posible, tal vez un simple cronjob?

¿Cómo podemos resolver este rompecabezas?

Dr. Beco
fuente
1
Tenga en cuenta que zsh hace esto fuera de la caja (y tiene muchas opciones para ajustar el intercambio de historial).
Gilles 'SO- deja de ser malvado'
Gracias @Gilles; Es gracioso, años usando Linux y nunca intenté nada más bash. Tal vez es hora de revisar algo nuevo, solo por diversión.
Dr Beco

Respuestas:

15

¿Señales creativas e implicantes, dices? OKAY:

trap on_exit EXIT
trap on_usr1 USR1

on_exit() {
    history -a
    trap '' USR1
    killall -u "$USER" -USR1 bash
}

on_usr1() {
    history -n
}

Tira eso .bashrcy vete. Utiliza señales para indicar a cada bashproceso que verifique si hay nuevas entradas en el historial cuando sale otra. Esto es bastante horrible, pero realmente funciona.


¿Como funciona?

trapestablece un controlador de señal para una señal del sistema o uno de los eventos internos de Bash. El EXITevento es cualquier terminación controlada del shell, mientras que USR1es SIGUSR1una señal sin sentido que nos estamos apropiando.

Cada vez que sale la cáscara, nosotros:

  • Agregue todo el historial al archivo explícitamente.
  • Inhabilite el SIGUSR1controlador y haga que este shell ignore la señal.
  • Envíe la señal a todos los bashprocesos en ejecución desde el mismo usuario.

Cuando SIGUSR1llega un , nosotros:

  • Cargue todas las entradas nuevas del archivo de historial en la lista de historial en memoria del shell.

Debido a la forma en que Bash maneja las señales, en realidad no obtendrá los nuevos datos del historial hasta que llegue Enterla próxima vez, por lo que esto no funciona mejor en ese frente que poner history -nen PROMPT_COMMAND. Sin embargo, guarda la lectura del archivo constantemente cuando no ha sucedido nada, y no hay ninguna escritura hasta que se cierra el shell.


Sin embargo, todavía hay un par de problemas aquí. La primera es que la respuesta predeterminada SIGUSR1es terminar el shell. Cualquier otro bashproceso (ejecutando scripts de shell, por ejemplo) será eliminado. .bashrcno se carga con shells no interactivos. En su lugar, se carga un archivo nombrado porBASH_ENV : puede configurar esa variable en su entorno globalmente para que apunte a un archivo con:

trap '' USR1

en él para ignorar la señal en ellos (que resuelve el problema).

Finalmente, aunque esto hace lo que pediste, el pedido que recibas será un poco inusual. En particular, los fragmentos de la historia se repetirán en diferentes órdenes a medida que se cargan y guardan por separado. Eso es esencialmente inherente a lo que está pidiendo, pero tenga en cuenta que el historial de flecha hacia arriba se vuelve mucho menos útil en este momento. Sin embargo, las sustituciones de historia y similares se compartirán y funcionarán bien.

Michael Homer
fuente
Me pregunto si habilitó la marca de tiempo dentro del .bashrcarchivo, y luego hizo que apareciera algo así como un cronjob y recurriera el archivo periódicamente, enviando un SIGUSR1fragmento según su fragmento, ¿podría recuperar el historial cronológico de flecha hacia arriba?
forquare
Es una solución ordenada, pero de hecho otros procesos terminan con USR1. ¿Este comportamiento también ocurre con USR2?
Dr Beco
Agregaré un segundo comentario, porque estoy asombrado. ¿Cómo es que el proceso asocia TERM con USR1? ¿No es USR1 algo para ser utilizado solo por el usuario? ¿Es esto un poco linux bug? ¿Por qué otros procesos están capturando USR1? (Sé que no está destinado a ser discutido aquí, pero tal vez sacaré esto a la luz en el lugar correcto)
Dr. Beco
El comportamiento predeterminado de casi todas las señales es terminar; se especifica en POSIX . No es un error. Sin embargo, Bash le permitirá especificar que la señal se ignorará en todos sus procesos, por lo que está bien para nuestros propósitos.
Michael Homer
Gracias @MichaelHomer. ¿Esta especificación a la que te refieres se hace BASH_ENVcomo se indica en esta respuesta? ¿O conoces una forma más estándar de apagar este mal?
Dr Beco