Histórico do Bash: "ignoredups" e "apagados", configurando conflito com o histórico comum nas sessões


84

Primeiro de tudo, isso não é uma duplicata de nenhum encadeamento existente no SE. Eu li esses dois tópicos ( , ) sobre uma melhor história do bash, mas nenhuma das respostas funciona - - eu estou no Fedora 15 a propósito.

.bashrcAdicionei o seguinte ao arquivo no diretório do usuário (/ home / aahan /), e ele não funciona. Alguém tem uma pista?

HISTCONTROL=ignoredups:erasedups  # no duplicate entries
HISTSIZE=1000                     # custom history size
HISTFILESIZE=100000                 # custom history file size
shopt -s histappend                      # append to history, don't overwrite it
PROMPT_COMMAND="history -a; history -c; history -r; $PROMPT_COMMAND"  # Save and reload the history after each command finishes

Ok, é isso que eu quero com o histórico do bash (prioridade):

  • não armazene duplicados, apague os existentes
  • compartilhe imediatamente o histórico com todos os terminais abertos
  • sempre acrescente o histórico, não o substitua
  • armazenar comandos de várias linhas como um único comando (que está desativado por padrão)
  • qual é o tamanho padrão do histórico e o tamanho do arquivo de histórico?

colisão! qualquer um? Nada funciona. Isso poderia ser um problema com o Fedora 15 (com o Gnome 3 e executando em uma máquina virtual host do Windows)?
09

Por favor, não "bata" nas postagens aqui. Se eles não estão sendo respondidos, você precisa perguntar com mais clareza, incluir as partes certas de pistas de contexto ou algo assim. Se você precisar de atenção em suas postagens, poderá emitir recompensas que ajudem mais pessoas a prestar mais atenção.
Caleb

11
Você tem certeza de que está usando o bash? ( echo $SHELL) As configurações funcionam se você as executar manualmente a partir do seu shell aberto? Obviamente, como eles funcionam para muitos outros, as configurações estão corretas, você as implementou de maneira errada. E nenhuma máquina virtual Fedora15 / Gnome3 / / tem pouco a ver com a função real de bash.
Caleb

@ Caleb, desculpe-me primeiro por bater na publicação. Além disso, tentei o meu melhor para ser muito claro. Não, eu não os enviei no shell. Eu apenas os colei em um .bashrcarquivo. Isso está errado? Você pode adicionar uma "resposta" a esta postagem com os comandos reais do shell? (por favor, tenha com meu Noob-dade.)
its_me

11
Tudo o que você escreve em scripts ou .bashrcsão comandos de shell reais. Scripts são apenas séries de comandos do shell. Além disso, a edição que você fez recentemente da remoção do exportbit foi uma má ideia, que deve ser mantida.
Caleb

Respostas:


119

Este é realmente um comportamento realmente interessante e confesso que subestimei bastante a pergunta no início. Mas primeiro os fatos:

1. O que funciona

A funcionalidade pode ser alcançada de várias maneiras, embora cada uma funcione de maneira um pouco diferente. Observe que, em cada caso, para que o histórico seja "transferido" para outro terminal (atualizado), é necessário pressionar Entero terminal, onde ele deseja recuperar o histórico.

  • Opção 1:

    shopt -s histappend
    HISTCONTROL=ignoredups
    PROMPT_COMMAND="history -a; history -n; $PROMPT_COMMAND"
    

    Isso tem duas desvantagens:

    1. No login (abertura de um terminal), o último comando do arquivo de histórico é lido duas vezes no buffer de histórico do terminal atual;
    2. Os buffers de diferentes terminais não permanecem sincronizados com o arquivo de histórico.
  • opção 2:

    HISTCONTROL=ignoredups
    PROMPT_COMMAND="history -a; history -c; history -r; $PROMPT_COMMAND"
    

    (Sim, não é necessário shopt -s histappende sim, ele deve estar history -c no meio de PROMPT_COMMAND) Esta versão também tem duas desvantagens importantes:

    1. O arquivo de histórico deve ser inicializado. Ele deve conter pelo menos uma linha não vazia (pode ser qualquer coisa).
    2. O historycomando pode dar uma saída falsa - veja abaixo.

[Edit] "E o vencedor é ..."

  • opção 3:

    HISTCONTROL=ignoredups:erasedups
    shopt -s histappend
    PROMPT_COMMAND="history -n; history -w; history -c; history -r; $PROMPT_COMMAND"
    

    Isto é o mais longe possível. É a única opção para que ambos erasedupse a história comum funcionem simultaneamente. Esta é provavelmente a solução final para todos os seus problemas, Aahan.


2. Por que a opção 2 parece não funcionar (ou: o que realmente não funciona como o esperado)?

Como mencionei, cada uma das soluções acima funciona de maneira diferente. Mas a interpretação mais enganosa de como as configurações funcionam vem da análise da saída do historycomando . Em muitos casos, o comando pode fornecer uma saída falsa . Por quê? Porque é executado antes da sequência de outros historycomandos contidos no PROMPT_COMMAND! No entanto, ao usar a segunda ou terceira opção, é possível monitorar as alterações de .bash_historyconteúdo (usando, watch -n1 "tail -n20 .bash_history"por exemplo) e ver qual é o histórico real.

3. Por que a opção 3 é tão complicada?

Tudo está no modo como erasedupsfunciona. Como o manual do bash afirma, "(...) erasedupsfaz com que todas as linhas anteriores correspondentes à linha atual sejam removidas da lista de histórico antes que a linha seja salva" . Então, isso é realmente o que o OP queria (e não apenas, como eu pensava anteriormente, não ter duplicatas aparecendo em sequência ) . Eis por que cada um dos history -.comandos tem ou não pode estar no PROMPT_COMMAND:

  • history -n precisa estar lá antes history -wpara ler .bash_historyos comandos salvos de qualquer outro terminal,

  • history -w precisa estar lá para salvar o histórico em arquivos e apagar duplicatas,

  • history -a não deve ser colocado lá em vez de history -w, porque não aciona a exclusão de duplicatas,

  • history -ctambém é necessário porque evita que o buffer do histórico seja destruído após cada comando,

  • e, finalmente, history -ré necessário restaurar o buffer do histórico a partir do arquivo, tornando o histórico compartilhado entre as sessões do terminal.


2
+1 para "Mas a interpretação mais enganosa de como as configurações funcionam vem da análise da saída do comando history". Eu acho que esse é o cerne da questão do OP. Excelente dedução.
Jasonwryan

2
Finalmente vi o seu ponto. Por 'duplicatas', você quis dizer algo além de mim. Eu me concentrei apenas em sequências do mesmo comando . Atualizei minha resposta - consulte "opção 3". Além disso, para o seu caso, para testar como o histórico funciona, você deve realmente usar em watch "tail -n 20 .bash_history"vez do tail -f .bash_history.
rozcietrzewiacz

2
Opção 3 apenas eliminou todos os meus 100.000 linhas da história :( (bash 3.2.25 (1) -release)
Felipe Alvarez

2
history -ctambém é necessário porque evita o descarte do buffer do histórico após cada comando Por que esse lixo ocorre?
Piotr Dobrogost 29/02

2
Sua solução 3 na verdade não funciona. É extremamente complicado e depende da ordem em que os usuários acessam entrar em diferentes terminais. Isso geralmente resulta em comandos perdidos, especialmente ao alternar entre terminais. Fonte: tentei.
6cef 12/09

8

No seu prompt de comando, você está usando o -ccomutador. De man bash:

-c   Limpa a lista do histórico excluindo todas as entradas

Para compartilhar seu histórico com todos os terminais abertos, você pode usar -n:

-n   Leia as linhas do histórico que ainda não foram lidas do arquivo de histórico na lista de histórico atual. Essas são linhas anexadas ao arquivo de histórico desde o início da sessão atual do bash.

O tamanho padrão também está no manual:

HISTSIZE O número de comandos a serem lembrados no histórico de comandos (consulte HISTORY abaixo). O valor padrão é 500.

Para salvar comandos de várias linhas:

A opção cmdhist shell, se ativada, faz com que o shell tente salvar cada linha de um comando de várias linhas na mesma entrada do histórico, adicionando ponto e vírgula quando necessário para preservar a correção sintática. A opção lithist shell faz com que o shell salve o comando com novas linhas incorporadas em vez de ponto e vírgula.

Além disso, você não deve preceder os comandos HIST * com export- eles são variáveis ​​apenas do bash, não variáveis ​​ambientais: HISTCONTROL=ignoredups:erasedupsé suficiente.


Então, isso export PROMPT_COMMAND="history -a; history -n; history -r; $PROMPT_COMMAND"está correto? Além disso, você sabe como posso fazer com que o arquivo de histórico armazene comandos de várias linhas como um único comando (que está desativado por padrão)?
its_me

11
Sim. Conforme a página de manual citada acima, shopt -s cmdhistele salvará várias linhas.
Jasonwryan

ok, eu tentei todos eles. Parece que HISTCONTROL=ignoredups:erasedupsnão está funcionando. Eu também tentei ter exatamente isso no .bashrcarquivo (entre funções personalizadas). Alguma ideia do que pode estar errado? Estou usando o Fedora 15 em uma máquina virtual - - Windows 7 host.
its_me

Verifique se não há nada nos outros arquivos de inicialização, como etc /etc/bashrc, .bash_profileque o esteja substituindo.
Jasonwryan

Não vejo nenhum problema com o .bash_profilearquivo. Quanto ao conteúdo em /etc/bashrcque eu colocá-lo aqui, por favor, dê uma olhada - pastebin.com/Uae6sE6s
its_me

8

Foi isso que eu criei e estou feliz com isso até agora ...

alias hfix='history -n && history | sort -k2 -k1nr | uniq -f1 | sort -n | cut -c8- > ~/.tmp$$ && history -c && history -r ~/.tmp$$ && history -w && rm ~/.tmp$$'  
HISTCONTROL=ignorespace  
shopt -s histappend  
shopt -s extglob  
HISTSIZE=1000  
HISTFILESIZE=2000  
export HISTIGNORE="!(+(*\ *))"  
PROMPT_COMMAND="hfix; $PROMPT_COMMAND" 

NOTAS:

  • Sim, é complicado ... mas remove todas as duplicatas e ainda preserva a cronologia dentro de cada terminal!
  • Meu HISTIGNOREignora todos os comandos que não têm argumentos. Isso pode não ser desejável para algumas pessoas e pode ser deixado de fora.


0

Não funciona, porque você esquece:

 -n   read all history lines not already read from the history file
      and append them to the history list

Mas parece que history -né apenas buggy quando export HISTCONTROL=ignoreboth:erasedupsestá em vigor.

Permite experimentar:

$ PROMPT_COMMAND=
$ export HISTCONTROL=ignoreboth:erasedups
$ export HISTFILE=~/.bash_myhistory
$ HISTIGNORE='history:history -w'
$ history -c
$ history -w

Aqui, ativamos a exclusão de dups, alternamos o histórico para um arquivo personalizado, limpamos o histórico. Após a conclusão de todos os comandos, temos o arquivo de histórico vazio e um comando no histórico atual.

$ history
$ cat ~/.bash_myhistory
$ history
$ 1  [2019-06-17 14:57:19] cat ~/.bash_myhistory

Abra o segundo terminal e execute esses seis comandos também. Depois disso:

$ echo "X"
$ echo "Y"
$ history -w
$ history
  1  [2019-06-17 15:00:21] echo "X"
  2  [2019-06-17 15:00:23] echo "Y"

Agora seu histórico atual possui dois comandos e o arquivo de histórico possui:

#1560772821
echo "X"
#1560772823
echo "Y"

Voltar ao primeiro terminal:

$ history -n
$ history
1  [2019-06-17 14:57:19] cat ~/.bash_myhistory 
2  [2019-06-17 15:03:12] history -n

Huh ... nenhum dos echocomandos é lido. Mude para o segundo terminal novamente e:

$ echo "Z"
$ history -w

Agora o arquivo de histórico é:

#1560772821
echo "X"
#1560772823
echo "Y"
#1560773057
echo "Z"

Mude para o primeiro terminal novamente:

$ history -n
$ history
  1  [2019-06-17 14:57:19] cat ~/.bash_myhistory 
  2  [2019-06-17 15:03:12] history -n
echo "Z"

Você pode ver que o echo "Z"comando está mesclado history -n.

Outro bug é por causa dos comandos que são lidos na história pelo número do comando e não pelo tempo do comando, eu acho. Espero que outros echocomandos apareçam na história


0

O erasedups não corta (como o chomp em alguns idiomas) os espaços iniciais e finais. Isso é um bug. Também as apagadas não excluem todas as entradas anteriores.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.