Como posso remover duplicatas na minha .bash_history, preservando a ordem?

51

Eu realmente gosto de usar control+r para pesquisar recursivamente meu histórico de comandos. Eu encontrei algumas boas opções que eu gosto de usar:

# ignore duplicate commands, ignore commands starting with a space
export HISTCONTROL=erasedups:ignorespace

# keep the last 5000 entries
export HISTSIZE=5000

# append to the history instead of overwriting (good for multiple connections)
shopt -s histappend

O único problema para mim é que erasedups apenas elimina duplicados sequenciais - de modo que com esta sequência de comandos:

ls
cd ~
ls

O comando ls será realmente gravado duas vezes. Eu pensei em executar periodicamente w / cron:

cat .bash_history | sort | uniq > temp.txt
mv temp.txt .bash_history

Isso conseguiria remover as duplicatas, mas infelizmente o pedido não seria preservado. Se eu não sort o arquivo primeiro, não acredito que uniq possa funcionar corretamente.

Como posso remover duplicatas na minha .bash_history, preservando a ordem?

Crédito extra:

Existe algum problema em sobrescrever o arquivo .bash_history por meio de um script? Por exemplo, se você remover um arquivo de log do Apache, será necessário enviar um sinal de nohup / reset com kill para liberá-lo para o arquivo. Se esse for o caso com o arquivo .bash_history , talvez eu possa de alguma forma usar ps para verificar e garantir que não haja sessões conectadas antes da execução do script de filtragem?

    
por cwd 20.09.2012 / 16:55

7 respostas

30

Classificando o histórico

Este comando funciona como sort|uniq , mas mantém as linhas no lugar

nl|sort -k 2|uniq -f 1|sort -n|cut -f 2

Basicamente, pré-anexa a cada linha seu número. Depois de sort|uniq -ing, todas as linhas são ordenadas de acordo com a ordem original (usando o campo de número de linha) e o campo de número de linha é removido das linhas.

Esta solução tem a falha que é indefinida, que representa uma classe de linhas iguais na saída e, portanto, sua posição na saída final é indefinida. No entanto, se o representante mais recente for escolhido, você poderá sort da entrada por uma segunda chave:

nl|sort -k2 -k 1,1nr|uniq -f1|sort -n|cut -f2

Gerenciando .bash_history

Para reler e escrever o histórico, você pode usar history -a e history -w , respectivamente.

    
por 20.09.2012 / 17:38
33

Então, eu estava procurando a mesma coisa depois de ser incomodado por duplicatas e descobri que, se eu editar meu ~ / .bash_profile (Mac) com:

export HISTCONTROL=ignoreboth:erasedups

Faz exatamente o que você queria, apenas mantém o mais recente de qualquer comando. ignoreboth é, na verdade, apenas como ignorespace:ignoredups e que junto com erasedups faz o trabalho.

Pelo menos no meu terminal Mac com bash este trabalho perfeito. Encontrou aqui em askubuntu.com .

    
por 25.02.2016 / 08:02
14

Encontrou esta solução na natureza e testou:

awk '!x[$0]++'

Na primeira vez que um valor específico de uma linha ($ 0) é visto, o valor de x [$ 0] é zero.
O valor de zero é invertido com ! e se torna um.
Uma declaração que avalia para um faz com que a ação padrão, que é imprimir.

Portanto, a primeira vez que um $0 específico é visto, ele é impresso.

Na próxima vez (as repetições), o valor de x[$0] foi obtido,
seu valor negado é zero e uma declaração que avalia a zero não imprime.

Para manter o último valor repetido, inverta o histórico e use o mesmo awk:

awk '!x[$0]++' ~/.bash_history                 # keep the first value repeated.

tac ~/.bash_history | awk '!x[$0]++' | tac     # keep the last.
    
por 10.06.2013 / 08:57
7

Estendendo a resposta de Clayton:

tac $HISTFILE | awk '!x[$0]++' | tac | sponge $HISTFILE

tac inverter o arquivo, verifique se você instalou moreutils , então você tem sponge disponível, caso contrário, use um arquivo temporário.

    
por 19.01.2015 / 11:23
4

Estes manteriam as últimas linhas duplicadas:

ruby -i -e 'puts readlines.reverse.uniq.reverse' ~/.bash_history
tac ~/.bash_history | awk '!a[$0]++' | tac > t; mv t ~/.bash_history
    
por 10.06.2013 / 17:06
1

Este é um post antigo, mas um problema perpétuo para usuários que desejam ter vários terminais abertos e ter o histórico sincronizado entre as janelas, mas não duplicado.

Minha solução em .bashrc:

shopt -s histappend
export HISTCONTROL=ignoreboth:erasedups
export PROMPT_COMMAND="history -n; history -w; history -c; history-r"
tac $HISTFILE | awk '!x[$0]++' | tac > /tmp/tmpfile ; /tmp/tmpfile > $HISTFILE
rm /tmp/tmpfile
    A opção
  • histappend adiciona o histórico do buffer ao final do arquivo de histórico ($ HISTFILE)
  • ignoreboth e erasedups evitam que entradas duplicadas sejam salvas no $ HISTFILE
  • O comando prompt atualiza o cache do histórico
    • histórico -n lê todas as linhas de $ HISTFILE que podem ter ocorrido em um terminal diferente desde o último retorno de carro
    • history -w escreve o buffer atualizado para $ HISTFILE
    • history -c limpa o buffer para que não ocorra duplicação
    • histórico -r relê o $ HISTFILE, anexando ao buffer em branco
  • o script awk armazena a primeira ocorrência de cada linha que encontra. tac inverte-o e, em seguida, inverte-o de volta para que possa ser salvo com os comandos mais recentes ainda mais recentes na história
  • rm o arquivo / tmp /

Toda vez que você abre um novo shell, o histórico é apagado, e toda vez que você pressiona a tecla Enter em um navegador diferente, ele atualiza esse histórico do arquivo.

    
por 26.01.2018 / 09:01
0

Para registrar todos os novos comandos é complicado. Primeiro você precisa adicionar ~/.profile ou similar:

HISTCONTROL=erasedups
PROMPT_COMMAND='history -w'

Então você precisa adicionar a ~/.bash_logout :

history -a
history -w
    
por 05.02.2018 / 13:31