Подтвердить что ты не робот

Предотвратить сохранение дубликатов в истории bash

Я пытаюсь предотвратить bash от сохранения повторяющихся команд в моей истории. Вот что у меня есть:

shopt -s histappend
export HISTIGNORE='&:ls:cd ~:cd ..:[bf]g:exit:h:history'
export HISTCONTROL=erasedups
export PROMPT_COMMAND='history -a'

Это работает отлично, пока я вошел в систему, а .bash_history - в памяти. Например:

$ history
    1 vi .bashrc
    2 vi .alias
    3 cd /cygdrive
    4 cd ~jplemme
    5 vi .bashrc
    6 vi .alias

$ vi .bashrc

$ history
    1 vi .alias
    2 cd /cygdrive
    3 cd ~jplemme
    4 vi .alias
    5 vi .bashrc

$ vi .alias

$ history
    1 cd /cygdrive
    2 cd ~jplemme
    3 vi .bashrc
    4 vi .alias

$ exit

Но когда я заходил в систему, мой файл истории выглядит следующим образом:

$ history
    1 vi .bashrc
    2 vi .alias
    3 cd /cygdrive
    4 cd ~jplemme
    5 vi .bashrc
    6 vi .alias
    7 vi .bashrc
    8 vi .alias

Что я делаю неправильно?

EDIT: удаление строк shopt и PROMPT_COMMAND из .bashrc не устраняет проблему.

4b9b3361

Ответ 1

Насколько я знаю, невозможно делать то, что вы хотите. Я вижу это как ошибку в обработке истории bash, которая может быть улучшена.

export HISTCONTROL=ignoreboth:erasedups   # no duplicate entries
shopt -s histappend                       # append history file
export PROMPT_COMMAND="history -a"        # update histfile after every command

Это сохранит историю в памяти уникально, но при сохранении истории из нескольких сеансов в один и тот же файл она не сохранит историю в самом файле. history -a будет записывать новую команду в файл, если она не будет такой же, как и непосредственно перед этим. Он не будет делать полное дедупликацию, как в erasedups, в памяти.

Чтобы увидеть эту глупость в действии, запустите новую сессию терминала, просмотрите историю, и вы увидите повторяющиеся записи, например ls. Теперь запустите команду ls, и все дублированные ls будут удалены из истории в памяти, оставив только последнюю. История памяти становится короче, поскольку вы запускаете команды, которые дублируются в файле истории, но сам файл истории продолжает расти.

Я использую свой собственный script для очистки файла истории по запросу.

# remove duplicates while preserving input order
function dedup {
   awk '! x[$0]++' [email protected]
}

# removes $HISTIGNORE commands from input
function remove_histignore {
   if [ -n "$HISTIGNORE" ]; then
      # replace : with |, then * with .*
      local IGNORE_PAT=`echo "$HISTIGNORE" | sed s/\:/\|/g | sed s/\*/\.\*/g`
      # negated grep removes matches
      grep -vx "$IGNORE_PAT" [email protected]
   else
      cat [email protected]
   fi
}

# clean up the history file by remove duplicates and commands matching
# $HISTIGNORE entries
function history_cleanup {
   local HISTFILE_SRC=~/.bash_history
   local HISTFILE_DST=/tmp/.$USER.bash_history.clean
   if [ -f $HISTFILE_SRC ]; then
      \cp $HISTFILE_SRC $HISTFILE_SRC.backup
      dedup $HISTFILE_SRC | remove_histignore >| $HISTFILE_DST
      \mv $HISTFILE_DST $HISTFILE_SRC
      chmod go-r $HISTFILE_SRC
      history -c
      history -r
   fi
}

Мне бы хотелось услышать более элегантные способы сделать это.

Примечание. script не будет работать, если вы включите отметку времени в истории через HISTTIMEFORMAT.

Bash может улучшить ситуацию на

  • fix history -a только для записи новых данных, если он не соответствует истории в памяти, а не только последней.
  • де-дедуплицировать историю при чтении файлов, если установлено значение erasedups. Простой history -w в новом терминале затем очистит файл истории, а не глупый script выше.

Ответ 2

Проблема - это, безусловно, histappend. Протестировано и подтверждено в моей системе.

Моя соответствующая среда:

$ set | grep HIST
HISTFILE=/Users/hop/.bash_history
HISTFILESIZE=500
HISTIGNORE=' *:&:?:??'
HISTSIZE=500
$ export HISTCONTROL=erasedups
$ shopt | grep hist
cmdhist         on
histappend      off
histreedit      off
histverify      off
lithist         off

Теперь, когда я думаю об этом, проблема, вероятно, связана с history -a. history -w должен записывать текущую историю без каких-либо дубликатов, поэтому используйте это, если вы не возражаете против проблем concurrency.

Ответ 3

export HISTCONTROL=ignoreboth

Ответ 4

Вот что я использую.

[[email protected] ~]$ grep HIST .alias*
.alias:HISTCONTROL="erasedups"
.alias:HISTSIZE=20000
.alias:HISTIGNORE=ls:ll:"ls -altr":"ls -alt":la:l:pwd:exit:mc:su:df:clear:ps:h:history:"ls -al"
.alias:export HISTCONTROL HISTSIZE HISTIGNORE
[[email protected] ~]$ 

и работает

[[email protected] ~]$ pwd
/Users/XXX
[[email protected] ~]$ pwd
/Users/XXX
[[email protected] ~]$ history | grep pwd | wc -l
       1

Ответ 5

внутри вашего .bash_profile добавить

alias hist="history -a && hist.py"

затем поместите это на свой путь как hist.py и сделайте его выполнимым

#!/usr/bin/env python
f = open('/Users/joe/.bash_history')
l = f.readlines()
l.reverse()
short = []
for s in l:
    if s.rstrip() not in short:
        short.append(s.rstrip())
short.reverse()
for s in short:
    print s

теперь, когда вам нужен короткий список, просто введите hist