Atualize o histórico do bash em outros terminais ao sair de um terminal

15

Eu sei que esta pergunta não é obscura, pois é solicitada aqui, continue atualizando (e duplicada aqui).

O que estou tentando alcançar é um pouco diferente. Não gosto da ideia de reescrever um arquivo a cada lsdigito ( history -a; history -c; history -r).

Gostaria de atualizar o arquivo na saída. Isso é fácil (na verdade, padrão), mas você precisa anexar em vez de reescrever:

shopt -s histappend

Agora, quando um terminal estiver fechado, gostaria de deixar todos os outros que permanecem abertos para conhecer a atualização.

Eu prefiro fazer isso sem verificar via $PS1todos os commandque eu digito. Eu acho que seria melhor capturar algum tipo de sinal. Como você faria isso? Se não for possível, talvez um simples cronjob?

Como podemos resolver esse quebra-cabeça?

Dr Beco
fonte
1
Observe que o zsh faz isso imediatamente (e tem muitas opções para ajustar o compartilhamento do histórico).
Gilles 'SO- stop be evil'
Obrigado @Gilles; Engraçado, anos usando o Linux e nunca tentei nada além disso bash. Talvez seja hora de verificar algo novo, apenas por diversão.
Dr Beco

Respostas:

15

Sinais criativos e envolventes, você diz? ESTÁ BEM:

trap on_exit EXIT
trap on_usr1 USR1

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

on_usr1() {
    history -n
}

Coloque isso .bashrce vá embora. Isso usa sinais para dizer a cada bashprocesso que verifique novas entradas no histórico quando outra sair. Isso é horrível, mas realmente funciona.


Como funciona?

trapdefine um manipulador de sinal para um sinal do sistema ou para um dos eventos internos do Bash. O EXITevento é qualquer terminação controlada do shell, enquanto USR1é SIGUSR1um sinal sem sentido que estamos nos apropriando.

Sempre que o shell sai, nós:

  • Anexe todo o histórico ao arquivo explicitamente.
  • Desative o SIGUSR1manipulador e faça esse shell ignorar o sinal.
  • Envie o sinal para todos os bashprocessos em execução do mesmo usuário.

Quando SIGUSR1chega, nós:

  • Carregue todas as novas entradas do arquivo de histórico na lista de histórico de memória do shell.

Por causa da maneira como o Bash lida com os sinais, você não obterá os novos dados do histórico até chegar Enterna próxima vez, para que isso não funcione melhor nessa frente do que history -nem colocar PROMPT_COMMAND. No entanto, economiza a leitura constante do arquivo quando nada aconteceu, e não há nenhuma gravação até que o shell saia.


No entanto, ainda existem alguns problemas aqui. A primeira é que a resposta padrão SIGUSR1é finalizar o shell. Quaisquer outros bashprocessos (executando scripts de shell, por exemplo) serão eliminados. .bashrcnão é carregado por shells não interativos. Em vez disso, um arquivo nomeado por BASH_ENVé carregado : você pode definir essa variável em seu ambiente globalmente para apontar para um arquivo com:

trap '' USR1

nele para ignorar o sinal neles (o que resolve o problema).

Finalmente, embora isso faça o que você pediu, a encomenda que você recebe será um pouco incomum. Em particular, pedaços do histórico serão repetidos em diferentes ordens, à medida que forem carregados e salvos separadamente. Isso é essencialmente inerente ao que você está pedindo, mas lembre-se de que o histórico da seta para cima se torna muito menos útil nesse momento. As substituições da história e similares serão compartilhadas e funcionarão bem.

Michael Homer
fonte
Gostaria de saber se você ativou o carimbo de data / hora no .bashrcarquivo e, em seguida, apareceu algo como um cronjob e recorreu ao arquivo periodicamente, enviando um SIGUSR1trecho conforme o seu caso, se você poderia recuperar o histórico cronológico da seta para cima?
forquare
É uma solução interessante, mas pelo fato de outros processos terminarem no USR1. Esse comportamento também acontece com USR2?
Dr Beco
Vou adicionar um segundo comentário, porque estou surpreso. Como o processo associa o TERM ao USR1? USR1 não é algo para ser usado apenas pelo usuário? Isso é algum linux bug?? Por que outro processo está pegando USR1? (Eu sei que não pretende ser discutido aqui, mas talvez eu vou trazer isso para a luz no lugar correto)
Dr Beco
O comportamento padrão de quase todos os sinais é terminar; está especificado no POSIX . Não é um bug. O Bash permitirá que você especifique que o sinal será ignorado em todos os seus processos, portanto, isso é bom para nossos propósitos.
Michael Homer
Obrigado @MichaelHomer. Esta especificação foi feita BASH_ENVcomo indicado nesta resposta? Ou você conhece uma maneira mais padrão de desligar esse mal?
Dr Beco