Histórico do BASH truncado para 500 linhas em cada login

22

Por alguma razão, não consigo fazer com que meu sistema mantenha meu histórico do BASH após uma reinicialização. Aqui estão as seções relevantes do meu ~/.bashrc:

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

Tanto quanto posso dizer, essas são todas as opções necessárias (eu sei que costumava manter o histórico em várias reinicializações sem todas elas no passado). No entanto, apesar de ter adicionado essas opções várias reinicializações atrás, ainda perco a maior parte do meu histórico após uma reinicialização. Não está vazio, mas não possui as 9999 linhas que eu tinha antes de reiniciar.

Antes que alguém reclame, sim, eu li essas perguntas. Eu implementei algumas de suas sugestões, conforme listado acima, o restante foi inútil ou não relevante:

Com a chance de haver outros comandos relevantes, você pode ver todo o meu ~/.bashrc aqui .

Então, o que estou perdendo? Por que minha história não é salva? Se alguém achar que outro arquivo pode ser relevante, informe-me e eu o publicarei. Eu verifiquei executando grep -i hist \.*no meu $HOMEque mostrou que o único .arquivo relevante contendo a string histou HISTera .bashrc.

Estou executando o Linux Mint Debian Edition, GNU bash, versão 4.2.36 (1) -release (x86_64-pc-linux-gnu) e meu emulador de terminal favorito (caso isso seja relevante) é terminator.


ATUALIZAR:

Seguindo a sugestão do @ mpy nos comentários, mudei meu ~/.bashrcpara definir HISTFILE=~/bash_historyem oposição ao padrão ~/.bash_historye isso parece resolver o problema de shells interativos . Os shells de logon ainda exibem o mesmo comportamento, com o histórico truncado nas 500linhas. No entanto, não há HISTvariáveis ​​relacionadas definidas nos arquivos 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

Então, por que é a criação HISTSIZEe HISTFILESIZEno ~/.bashrcnão é suficiente, a menos que explicitamente definir o $HISTFILEpara algo diferente do padrão ~/.bash_history?

Terdon
fonte
Você é o proprietário de .bash_history ou root? Fazer ls -l .bash_history
Adnan Bhatti
11
@MSStp sim, é de minha propriedade. Obrigado pela sugestão, mas não vejo como isso poderia ser um problema de permissão, ou tenho acesso de leitura / gravação a ela ou não. Como alguma história é salva, eu claramente faço. Se o bash tivesse uma configuração que causasse problemas quando esse arquivo não pertencesse ao usuário, ele reclamaria ou a funcionalidade do histórico inteiro não funcionaria.
terdon
Quando você executa o historycomando, a saída que você vê é idêntica à que você vê, executando cat .bash_history, além dos números de linha? Quero dizer, o historycomando lista os carimbos de hora ou outras informações? A razão pela qual estou perguntando é: se você vê essas coisas esotéricas, significa que há outro módulo / função / programa, que está mexendo com o histórico do shell e uma versão errada ou com erros do que quer que seja, pode estar causando sofrimento. .
MelBurslan
@Mel_Burslan sim, é a mesma coisa, a única diferença são os números das linhas.
terdon
2
Ok, uma sugestão um pouco estranha, mas também é um problema estranho ;): tente outro arquivo como HISTFILE, não o padrão ~/.bash_history. Explicação muito construída: Eu assumo que bash é seu shell padrão, portanto, ao iniciar o sistema, um shell não interativo é o pai da sua sessão X (eu também presumo que você use X), que não saberá nada sobre a opção histappend (como .bashrc é apenas lido por shells interativos), desde que esse shell pai funcione, tudo está bem, mas após o término (ou seja, parada do sistema), ele substitui ~/.bash_history(que é o padrão) e atrapalha o seu histórico ...
mpy:

Respostas:

17

O problema realmente se resume ao comportamento diferente dos shells de login e não-login. Eu havia definido as variáveis ​​que controlam o histórico no meu ~/.bahsrc. Este arquivo não é lido quando se inicia um shell de logon, é lido apenas por shells interativos e sem logon (de man bash):

Quando o bash é chamado como um shell de logon interativo ou como um shell não interativo com a --loginopção, ele primeiro lê e executa comandos do arquivo /etc/profile, se esse arquivo existir. Depois de ler esse arquivo, ele procura ~ / .bash_profile,, ~/.bash_logine ~/.profile, nessa ordem, e lê e executa comandos do primeiro que existe e é legível. A --noprofileopção pode ser usada quando o shell é iniciado para inibir esse comportamento.

[. . . ]

Quando um shell interativo que não é um shell de login é iniciado, o bash lê e executa comandos de ~ / .bashrc, se esse arquivo existir. Isso pode ser inibido usando a opção --norc. A opção --rcfile file forçará o bash a ler e executar comandos do arquivo em vez de ~ / .bashrc.

Portanto, toda vez que eu .historyfazia login, reduzia para um tty ou usava ssh, o arquivo ficava truncado porque eu também não havia definido o tamanho certo ~/.profile. Eu finalmente percebi isso e simplesmente coloquei as variáveis ~/.profile onde elas pertencem , em vez de~/.bashrc

Portanto, a razão pela qual eu ~/.historyestava sendo truncado foi porque eu havia definido apenas as variáveis ​​HISTORY em um arquivo lido por shells interativos e sem login e, portanto, toda vez que eu executava um tipo diferente de shell, as variáveis ​​eram ignoradas e o arquivo cortado. adequadamente.

Terdon
fonte
Muito bom ponto, obrigado por compartilhar! No entanto, não concordo com: Este arquivo não é lido quando se inicia um shell de login, é lido apenas por shells interativos. Porque um shell de login também pode ser interativo. Caso contrário, todo o mecanismo da história não faria sentido. O IMHO .bashrcnão é lido por (shells de login OU não interativos).
Mpy 14/05
@ mpy De fato, desculpe, eu quis dizer conchas interativas e sem login. Resposta editada.
terdon 14/05
11
@terdon, o idioma comum é que as opções interativas do shell não entram em ~ / .profile ou ~ / .bash_profile. Opções de shell interativas são colocadas em ~ / .bashrc. Para evitar a necessidade de manter as configurações em dois locais, os seguintes comandos podem ser colocados na parte superior de um ~ / .bash_profile: export BASH_ENV=~/.bashrc ; if [ -f ~/.bashrc ]; then . ~/.bashrc; fi... e na parte superior do ~ / .bashrc, marque para verificar se você realmente está executando interativamente: [ -z "$PS1" ] && return... Claro, é apenas um idioma.
Noah Spurrier
11
O @NoahSpurrier BASH_ENVnão é relevante, afeta apenas shells não interativos. Quanto ao "idioma", é algo que o Debian iniciou e com o qual eu pessoalmente não concordo. Muitas vezes tenho opções gráficas ( xsete similares) no meu .bashrc e não as quero ativas quando executo shells de login de um tty ou através dele ssh. Eu quero o meu profile e .bashrc separar. Muitos (embora não todos) os gerenciadores de login originam .profile quando você faz login, para que as variáveis ​​globais sejam definidas melhor onde serão lidas apenas uma vez e não sempre que você abrir um terminal.
terdon
11
@WilsonF yes. Os arquivos são lidos sequencialmente e os arquivos pessoais ( ~/.profileou ~/.bashrc) são lidos por último. Tudo o que estiver definido nesses itens terá precedência sobre as configurações globais. Você está certo, não deveria definir essas variáveis /etc/bash.bashrc. Use ~/.profile(ou ~/.bash_profile) em vez disso.
terdon
11

Minha sugestão é usar outro arquivo como HISTFILE, não o padrão ~/.bash_history.

Embora eu não tenha uma explicação analítica, tentarei descrever o que me levou a essa sugestão: Se você usa bashcomo shell padrão (login) e também usa X(o que é muito provável), você tem uma bashinstância em execução logo após o (gráfico). ) Conecte-se:

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

Eu acho que essa instância é um shell de logon, por isso não lê o seu ~/.bashrce, portanto, não sabe nada sobre a histappendopção:

man bash (1) : Quando um shell interativo que não é um shell de login é iniciado, o bash lê e executa comandos de /etc/bash.bashrc e ~ / .bashrc, se esses arquivos existirem. (...)

Enquanto esse "shell pai" for executado, tudo estará bem, mas após o término (ou seja, parada do sistema), ele substituirá ~/.bash_history(porque esse é o valor padrão) e atrapalha o seu histórico ou o prende no início do sistema para (novamente padrão) 500 linhas. (Ou talvez ambos ...)

Também me parece que não é suficiente incluir a configuração do histórico ~/.bashrc, pois essa não deve ser uma configuração incomum. Não tenho explicação para isso.


Com relação ao seu problema, que "Os shells de login ainda exibem o mesmo comportamento", você pode tentar incluir a configuração do histórico também em ~/.bash_profile:

man bash (1) : Quando o bash é chamado como um shell de login interativo ou como um shell não interativo com a opção --login, ele primeiro lê e executa comandos do arquivo / etc / profile, se esse arquivo existir. Depois de ler esse arquivo, ele procura ~ / .bash_profile, (...)

Infelizmente não posso postar uma explicação mais justificada com detalhes da minha própria bashconfiguração, pois sou um zshcara ...

mpy
fonte
2
Definir explicitamente as opções de histórico em meu ~/.bash_profileresolveu o problema. Agora estou usando ~/.bash_historycomo meu arquivo de histórico, mas simplesmente adicionei todas as linhas ~/.bashrcmostradas na minha pergunta para ~/.bash_profile. Ainda não tenho certeza de quem estava estragando as conchas interativas, mas parece funcionar agora, obrigado!
terdon
11
Desculpe não aceitar, mas esqueci de postar uma resposta explicando o que estava acontecendo. Eu descobri isso com alguma ajuda do @Gilles há um tempo e finalmente consegui postar uma resposta. Eu queria aceitar o meu porque, na verdade, explica o problema em vez de oferecer uma solução (muito boa) e poderia ajudar futuros visitantes.
terdon 14/05
2

Como todas as suas configurações estão em ordem, de acordo com a página de manual, e como o arquivo de histórico não é restrito pelo tamanho (bytes), a única explicação possível em que posso pensar. Tem a ver com a forma como a concha morre.

De acordo com a referência online, a saída normal (histórico salvo) ocorre apenas quando o shell recebe o SIGHUP. Realmente não consigo explicar como seu sistema propaga sinais quando reinicializado, mas suspeito que seu shell seja encerrado com SIGKILL ou SIGPWR.

Pode ser porque o seu WM é executado de forma assíncrona (espera) e o emulador de terminal é gerado a partir do WM, onde o bash está, recebe um sinal de força de saída diferente de SIGHUP. Também pode ser que o sistema operacional envie rapidamente o "kill final" a todos os processos antes que o SIGHUP inicial gracioso consiga chegar ao shell via X -> WM -> xterm, possivelmente porque X ou WM leva mais tempo para sair do que é preciso que o sistema operacional esteja pronto para cair.

Estou em águas profundas com essas coisas, mas acho que algo nesse sentido causa o comportamento errático. Eu já tive esse problema antes, e o remédio mais sólido é exitno bash, onde você deseja manter a história.

Percebi history -ana sua pergunta e não consigo pensar por que isso não seria suficiente para preservar a história.

Você pode solucionar o problema descobrindo o que realmente mata o seu bash e passando a descobrir onde o sinal se origina e corrigindo o problema lá, ou simplesmente liberar o histórico quando souber qual sinal é o último (supondo que os discos ainda estejam on-line nesse momento) ):

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...

A captura de tela incluída ilustra o que estou falando no segundo e no terceiro parágrafos. A sequência em que eu concordo da esquerda , mato a concha esquerda da direita e gata a história.

festança do homem

Na inicialização, (...) O arquivo nomeado pelo valor de HISTFILE é truncado, se necessário, para conter não mais que o número de linhas especificado pelo valor de HISTFILESIZE (+ padrão 500).

Se a opção shell histappend estiver ativada (+ padrão aqui), as linhas serão anexadas ao arquivo de histórico, caso contrário, o arquivo de histórico será substituído.

referência online

3.7.6 Sinais

Quando o Bash é interativo, na ausência de interceptações, ele ignora o SIGTERM (para que 'kill 0' não mate um shell interativo) e o SIGINT é capturado e tratado (para que a espera incorporada seja interrompida). Quando o Bash recebe um SIGINT, ele interrompe todos os loops em execução. Em todos os casos, o Bash ignora SIGQUIT. Se o controle de tarefas estiver em vigor (consulte Controle de tarefas), o Bash ignorará SIGTTIN, SIGTTOU e SIGTSTP.

Os comandos não integrados iniciados pelo Bash têm manipuladores de sinal configurados para os valores herdados pelo shell de seu pai. Quando o controle da tarefa não está em vigor, os comandos assíncronos ignoram SIGINT e SIGQUIT, além desses manipuladores herdados. Os comandos executados como resultado da substituição de comando ignoram os sinais de controle de tarefa gerados pelo teclado SIGTTIN, SIGTTOU e SIGTSTP.

O shell sai por padrão após o recebimento de um SIGHUP. Antes de sair, um shell interativo reenvia o SIGHUP para todos os trabalhos, em execução ou interrompidos. Os trabalhos interrompidos são enviados SIGCONT para garantir que eles recebam o SIGHUP. Para impedir que o shell envie o sinal SIGHUP para um trabalho específico, ele deve ser removido da tabela de trabalhos com o renegado incorporado (consulte Job Control Builtins) ou marcado para não receber SIGHUP usando disown -h.

Se a opção de shell huponexit tiver sido definida com shopt (consulte The Shopt Builtin), o Bash enviará um SIGHUP para todos os trabalhos quando um shell de logon interativo sair.

Se o Bash estiver aguardando a conclusão de um comando e receber um sinal para o qual uma captura foi configurada, a captura não será executada até que o comando seja concluído. Quando o Bash está aguardando um comando assíncrono por meio da espera incorporada, a recepção de um sinal para o qual uma captura foi configurada fará com que a espera incorporada retorne imediatamente com um status de saída maior que 128, imediatamente após o qual a captura é executada.

captura de tela demonstrativa

sinais

Ярослав Рахматуллин
fonte
Eu realmente não entendo o que você está fazendo nessa captura de tela. O /tmp/psoque você está matando? Entendo o seu ponto de vista sobre os diferentes sinais de morte (embora, como você diz, pensei que é isso que history -aexiste para lidar). Vou testar isso por um tempo e reportar de volta.
terdon
$ Tmp cat / ps * \ n ps -o pid = | cabeça -N1> ~ / tmp / pso \ n 17201 \ n
Ярослав Рахматуллин
0

Verifique / etc / profile e /etc/profile.d/*

Talvez haja algo mexendo com as configurações de histórico lá.

mabagu
fonte
Obrigado, mas grep -r HIST /etc/profile.d/não retorna nada, e eu já verifiquei /etc/profile.
terdon