BASH historial truncado a 500 líneas en cada inicio de sesión

22

Por alguna razón, no puedo hacer que mi sistema mantenga mi historial de BASH después de un reinicio. Aquí están las secciones relevantes de mi ~/.bashrc:

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

Por lo que puedo decir, esas son todas las opciones necesarias ( que solía poder mantener el historial en varios reinicios sin todo esto en el pasado). Sin embargo, a pesar de haber agregado estas opciones hace varios reinicios, todavía pierdo la mayor parte de mi historial después de un reinicio. No está vacío, pero no tiene las 9999 líneas que tenía antes de reiniciar.

Antes de que nadie se queje, sí, he leído estas preguntas. He implementado algunas de sus sugerencias como se mencionó anteriormente, el resto no fueron útiles o no relevantes:

En el caso de que pueda haber otros comandos relevantes allí, puede ver mi entero ~/.bashrc aquí .

Entonces, ¿qué me estoy perdiendo? ¿Por qué no se guarda mi historia? Si alguien piensa que otro archivo puede ser relevante, hágamelo saber y lo publicaré. Lo comprobé ejecutando grep -i hist \.*mi $HOMEque mostró que el único .archivo relevante que contiene la cadena histo HISTera .bashrc.

Estoy ejecutando Linux Mint Debian Edition, GNU bash, versión 4.2.36 (1) -release (x86_64-pc-linux-gnu) y mi emulador de terminal favorito (en caso de que sea relevante) es terminator.


ACTUALIZAR:

Siguiendo la sugerencia de @ mpy en los comentarios, cambié mi ~/.bashrcconfiguración para establecerla HISTFILE=~/bash_historyen lugar de la predeterminada ~/.bash_historyy eso parece resolver el problema de los shells interactivos . Los shells de inicio de sesión todavía muestran el mismo comportamiento, con el historial truncado en las 500líneas. Sin embargo, no hay HISTvariables relacionadas establecidas en los archivos relevantes:

$ 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

Entonces, ¿por qué la configuración HISTSIZEy HISTFILESIZEen el ~/.bashrcno es suficiente a menos que establezca explícitamente $HISTFILEalgo diferente al predeterminado ~/.bash_history?

terdon
fuente
¿Eres el propietario de .bash_history o root? Do ls -l .bash_history
Adnan Bhatti
1
@MSStp sí, es de mi propiedad. Gracias por la sugerencia, pero no veo cómo podría ser un problema de permisos de todos modos, o tengo acceso de lectura / escritura o no. Como se guarda algo de historia, claramente lo hago. Si bash tuviera una configuración que causara problemas cuando este archivo no es propiedad del usuario, se quejaría o la funcionalidad del historial completo no funcionaría.
terdon
cuando ejecuta el historycomando, ¿la salida que ve es idéntica a la que ve, en ejecución cat .bash_history, además de los números de línea? Quiero decir, ¿el historycomando muestra marcas de tiempo u otra información? La razón por la que le pregunto es que si ve estas cosas esotéricas, significa que hay otro módulo / función / programa, que está interfiriendo con el historial de shell y una versión incorrecta o defectuosa de lo que sea, podría estar causándole dolor. .
MelBurslan
@Mel_Burslan sí, es lo mismo, la única diferencia son los números de línea.
terdon
2
Ok, una sugerencia un tanto extraña, pero también es un problema extraño ;): pruebe con otro archivo como HISTFILE, no el predeterminado ~/.bash_history. Explicación muy construida: supongo que bash es su shell predeterminado, por lo que al iniciar el sistema, un shell no interactivo es el padre de su sesión X (también supongo que usa X), que no sabrá nada sobre la opción histappend (ya que .bashrc es solo leer por shells interactivos), por lo tanto, mientras este shell padre se ejecute todo está bien, pero al finalizar (es decir, detener el sistema) se anulará ~/.bash_history(que es el valor predeterminado) y desordenará su historial ...
mpy

Respuestas:

17

El problema realmente se reduce al comportamiento diferente de los shells de inicio de sesión y no inicio de sesión. Había establecido las variables que controlan el historial en mi ~/.bahsrc. Este archivo no se lee cuando se inicia un shell de inicio de sesión, solo se lee mediante shells interactivos sin inicio de sesión (desde man bash):

Cuando se invoca bash como un shell de inicio de sesión interactivo, o como un shell no interactivo con la --loginopción, primero lee y ejecuta comandos del archivo /etc/profile, si ese archivo existe. Después de leer ese archivo, busca ~ / .bash_profile, ~/.bash_loginy ~/.profile, en ese orden, y lee y ejecuta comandos del primero que existe y es legible. La --noprofileopción se puede usar cuando se inicia el shell para inhibir este comportamiento.

[. . . ]

Cuando se inicia un shell interactivo que no es un shell de inicio de sesión, bash lee y ejecuta comandos desde ~ / .bashrc, si ese archivo existe. Esto puede inhibirse utilizando la opción --norc. La opción de archivo --rcfile obligará a bash a leer y ejecutar comandos desde el archivo en lugar de ~ / .bashrc.

Por lo tanto, cada vez que inicie sesión, o caiga en un tty, o use ssh, el .historyarchivo se trunca porque no lo configuré en el tamaño correcto ~/.profiletambién. Finalmente me di cuenta de esto y simplemente establecí las variables en ~/.profile donde pertenecen , en lugar de~/.bashrc

Entonces, la razón por la cual mi ~/.historytruncamiento se debía a que solo había configurado las variables HISTORIAL en un archivo leído por shells interactivos, sin inicio de sesión y, por lo tanto, cada vez que ejecutaba un tipo diferente de shell, las variables se ignoraban y el archivo se cortaba en consecuencia.

terdon
fuente
Muy buen punto, gracias por compartir! Sin embargo, no estoy de acuerdo con: Este archivo no se lee cuando se inicia un shell de inicio de sesión, solo se lee mediante shells interactivos. Porque un shell de inicio de sesión también puede ser interactivo. De lo contrario, todo el mecanismo de la historia no tendría sentido. La OMI .bashrcno es leída por (shells no interactivos O de inicio de sesión).
mpy
@mpy De hecho, lo siento, me refería a shells interactivos sin inicio de sesión. Respuesta editada.
terdon
1
@terdon, el idioma común es que las opciones de shell interactivo no van en ~ / .profile o ~ / .bash_profile. Las opciones de shell interactivo se colocan en ~ / .bashrc. Para evitar tener que mantener la configuración en dos lugares, los siguientes comandos se pueden colocar en la parte superior de un ~ / .bash_profile: export BASH_ENV=~/.bashrc ; if [ -f ~/.bashrc ]; then . ~/.bashrc; fi... y en la parte superior de ~ / .bashrc, marque para asegurarse de que realmente se está ejecutando de forma interactiva: [ -z "$PS1" ] && return... Por supuesto, es solo un idioma.
Noah Spurrier
1
@NoahSpurrier BASH_ENVno es relevante, solo afecta a los shells no interactivos. En cuanto al "idioma", es algo que Debian comenzó y con el que personalmente no estoy de acuerdo. A menudo tengo opciones gráficas ( xsety similares) en mi .bashrc y no quiero que estén activas cuando ejecuto shells de inicio de sesión, ya sea desde un tty o a través de ssh. Yo quiero mi .profile y Bashrc separado. Muchos (aunque no todos) los administradores de inicio de sesión obtienen el perfil .profile cuando inicia sesión, por lo que las variables globales se configuran mejor allí donde solo se leerán una vez y no cada vez que abra un terminal.
Terdon
1
@WilsonF sí. Los archivos se leen secuencialmente y los archivos personales ( ~/.profileo ~/.bashrc) se leen al final. Cualquier cosa establecida en ellos tendrá prioridad sobre la configuración global. Tienes toda la razón, no deberías configurar esas variables /etc/bash.bashrc. Use ~/.profile(o ~/.bash_profile) en su lugar.
terdon
11

Mi sugerencia es usar otro archivo como HISTFILE, no el predeterminado ~/.bash_history.

Aunque no tengo una explicación analítica, trataré de describir lo que me llevó a esta sugerencia: si usa bashsu shell predeterminado (inicio de sesión) y también usa X(lo cual es muy probable), tiene una bashinstancia en ejecución justo después de la (gráfica ) iniciar sesión:

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

Creo que esta instancia es un shell de inicio de sesión, por lo que no lee su ~/.bashrcy, por lo tanto, no sabrá nada sobre la histappendopción:

man bash (1) : cuando se inicia un shell interactivo que no es un shell de inicio de sesión , bash lee y ejecuta comandos desde /etc/bash.bashrc y ~ / .bashrc, si existen estos archivos. (...)

Mientras se ejecute este "shell principal", todo estará bien, pero al finalizar (es decir, detener el sistema) se anulará ~/.bash_history(porque ese es el valor predeterminado) y desordenará su historial o lo recortará en el inicio del sistema (nuevamente predeterminado) 500 líneas. (O tal vez ambos ...)

También me parece que no es suficiente incluir la configuración del historial ~/.bashrc, ya que esta no debería ser una configuración tan poco común. No tengo explicación para eso.


Con respecto a su problema, que "los shells de inicio de sesión todavía muestran el mismo comportamiento", puede intentar incluir la configuración del historial también en ~/.bash_profile:

man bash (1) : cuando se invoca bash como un shell de inicio de sesión interactivo o como un shell no interactivo con la opción --login, primero lee y ejecuta comandos del archivo / etc / profile, si ese archivo existe. Después de leer ese archivo, busca ~ / .bash_profile, (...)

Lamentablemente, no puedo publicar una explicación más justificada con detalles de mi propia bashconfiguración, ya que soy un zshchico ...

mpy
fuente
2
Establecer explícitamente las opciones de historial en mi ~/.bash_profileresolvió el problema. Ahora estoy usando ~/.bash_historycomo mi archivo de historial, pero simplemente he agregado todas las líneas que se ~/.bashrcmuestran en mi pregunta ~/.bash_profile. Todavía no estoy seguro de quién estaba arruinando los shells interactivos, pero parece funcionar ahora, ¡gracias!
terdon
1
Lamento no aceptarlo, pero olvidé publicar una respuesta explicando lo que estaba sucediendo. Lo descubrí con ayuda de @Gilles hace un tiempo y finalmente pude publicar una respuesta. Quería aceptar el mío porque en realidad explica el problema en lugar de ofrecer una solución (muy buena) y podría ayudar a los futuros visitantes.
terdon
2

Dado que todas sus configuraciones están en orden de acuerdo con la página de manual, y dado que el archivo de historial no está restringido por tamaño (bytes), la única explicación posible que se me ocurre. Tiene que ver con cómo muere el caparazón.

Según la referencia en línea, la salida elegante (historial guardado) ocurre solo cuando el shell recibe SIGHUP. Realmente no puedo explicar cómo su sistema propaga señales cuando se reinicia, pero sospecho que su shell se cierra con SIGKILL o SIGPWR.

Podría deberse a que su WM se ejecuta de forma asíncrona (espera) y el emulador de terminal se generó desde el WM donde bash recibe una señal de salida forzada que no es SIGHUP. También podría ser que el sistema operativo envíe rápidamente la "eliminación final" a todos los procesos antes de que el SIGHUP inicial elegante logre llegar al shell a través de X -> WM -> xterm, posiblemente porque X o WM tardan más en salir que se necesita que el sistema operativo esté listo para fallar.

Estoy en aguas profundas con estas cosas, pero creo que algo en ese sentido causa un comportamiento errático. He tenido este problema antes, y el remedio más sólido es exiten bash donde quieres mantener el historial.

Me di cuenta history -aen su pregunta, y no puedo pensar por qué eso no sería suficiente para preservar la historia.

Puede solucionar el problema averiguando qué es lo que realmente mata a su golpe y pasar a averiguar dónde se origina la señal y solucionar el problema allí, o simplemente borrar el historial cuando sepa qué señal es la última (suponiendo que los discos todavía estén en línea para entonces ):

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 captura de pantalla incluida ilustra de lo que estoy hablando en el segundo y tercer párrafos. La secuencia allí es I shell in from left , mata el shell izquierdo de la derecha y cat el historial.

hombre bash

Al inicio, (...) El archivo nombrado por el valor de HISTFILE se trunca, si es necesario, para que no contenga más que el número de líneas especificado por el valor de HISTFILESIZE (+ valor predeterminado 500).

Si la opción de shell histappend está habilitada (+ predeterminada aquí), las líneas se agregan al archivo de historial; de lo contrario, el archivo de historial se sobrescribe.

referencia en línea

3.7.6 Señales

Cuando Bash es interactivo, en ausencia de trampas, ignora SIGTERM (para que 'kill 0' no mate a un shell interactivo), y SIGINT es capturado y manejado (de modo que la espera incorporada es interrumpible). Cuando Bash recibe un SIGINT, se libera de cualquier bucle en ejecución. En todos los casos, Bash ignora SIGQUIT. Si el control de trabajo está vigente (ver Control de trabajo), Bash ignora SIGTTIN, SIGTTOU y SIGTSTP.

Los comandos no incorporados iniciados por Bash tienen manejadores de señal establecidos en los valores heredados por el shell de su padre. Cuando el control de trabajo no está en vigor, los comandos asincrónicos ignoran SIGINT y SIGQUIT además de estos controladores heredados. Los comandos ejecutados como resultado de la sustitución de comandos ignoran las señales de control de trabajo generadas por teclado SIGTTIN, SIGTTOU y SIGTSTP.

El shell sale por defecto al recibir un SIGHUP. Antes de salir, un shell interactivo reenvía el SIGHUP a todos los trabajos, en ejecución o detenidos. Los trabajos detenidos se envían SIGCONT para garantizar que reciban el SIGHUP. Para evitar que el shell envíe la señal SIGHUP a un trabajo en particular, debe eliminarse de la tabla de trabajos con el comando incorporado Desown (ver Trabajos incorporados de control) o marcado para no recibir SIGHUP usando disown -h.

Si la opción de shell huponexit se ha configurado con shopt (consulte The Shopt Builtin), Bash envía un SIGHUP a todos los trabajos cuando sale de un shell de inicio de sesión interactivo.

Si Bash está esperando que se complete un comando y recibe una señal para la cual se ha establecido una trampa, la trampa no se ejecutará hasta que se complete el comando. Cuando Bash está esperando un comando asincrónico a través del sistema de espera incorporado, la recepción de una señal para la cual se ha establecido una trampa hará que el sistema de espera regrese inmediatamente con un estado de salida superior a 128, inmediatamente después de lo cual se ejecuta la trampa.

captura de pantalla demostrativa

señales

Ярослав Рахматуллин
fuente
Realmente no entiendo lo que estás haciendo en esa captura de pantalla. ¿Qué es lo /tmp/psoque estás matando? Veo su punto sobre las diferentes señales de muerte (aunque, como usted dice, pensé que eso es lo history -aque hay que tratar). Probaré eso por un tiempo e informaré de nuevo.
terdon
$ cat tmp / ps * \ n ps -o pid = | head -n1> ~ / tmp / pso \ n 17201 \ n
Ярослав Рахматуллин
0

Verifique / etc / profile y /etc/profile.d/*

Tal vez hay algo que interfiere con la configuración del historial allí.

mabagu
fuente
Gracias, pero grep -r HIST /etc/profile.d/no devuelve nada, y ya lo he comprobado /etc/profile.
terdon