История Bash: параметры ignoredups и erasedups конфликтуют с общей историей между сеансами


85

Прежде всего, это не дубликат существующих потоков в SE. Я читал эти две темы ( 1-я , 2-я ) о лучшей истории Bash, но ни один из ответов не работает - - Кстати, я нахожусь на Fedora 15.

Я добавил следующее в .bashrcфайл в каталоге пользователя (/ home / aahan /), и он не работает. У кого-нибудь есть подсказка?

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

Хорошо, это то, что я хочу с историей bash (приоритет):

  • не храните дубликаты, удалите все существующие
  • немедленно делиться историей со всеми открытыми терминалами
  • всегда добавлять историю, а не переписывать
  • хранить многострочные команды как одну команду (по умолчанию она отключена)
  • каков размер истории по умолчанию и размер файла истории?

удар! кто угодно? Ничего не работает Может ли это быть проблемой с Fedora 15 (с Gnome 3 и работающей на виртуальной машине хоста Windows)?
its_me

Пожалуйста, не "поднять" сообщения здесь. Если они не получают ответа, вам нужно спросить более четко, включить правильные биты контекстных подсказок или что-то в этом роде. Если вам нужно внимание к своим постам, вы можете выплачивать награды, которые помогут привлечь больше людей, уделяя им больше внимания.
Калеб

1
Вы уверены, что даже используете bash? ( echo $SHELL). Работают ли настройки, если вы запускаете их вручную из открытой оболочки? Очевидно, что поскольку они работают для многих других, настройки верны, вы просто неправильно их реализуете. И никакая Fedora15 / Gnome3 /, являющаяся виртуальной машиной, не имеет ничего общего с реальной функцией bash.
Калеб

@Caleb, сначала извини за пост. Кроме того, я старался изо всех сил, чтобы быть очень ясным. Нет, я не выпустил их в оболочке. Я просто скопировал их в .bashrcфайл. Это неправильно? Можете ли вы добавить «ответ» к этому сообщению с помощью реальных команд оболочки? (пожалуйста,
потерпите мою невинность

1
Все, что вы пишете в сценариях или являются .bashrcфактическими командами оболочки. Скрипты - это просто серии команд оболочки. Также редактирование, которое вы недавно сделали, удалив exportбит, было плохой идеей, которую следует сохранить.
Калеб

Ответы:


120

На самом деле это действительно интересное поведение, и, признаюсь, в начале я сильно недооценил вопрос. Но сначала факты:

1. Что работает

Функциональность может быть достигнута несколькими способами, хотя каждый работает немного по-своему. Обратите внимание, что в каждом случае, чтобы история «передавалась» в другой терминал (обновлялся), нужно нажать Enterв терминале, где он / она хочет получить историю.

  • Опция 1:

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

    Это имеет два недостатка:

    1. При входе в систему (открытии терминала) последняя команда из файла истории дважды считывается в буфер истории текущего терминала;
    2. Буферы разных терминалов не синхронизируются с файлом истории.
  • вариант 2:

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

    (Да, нет необходимости shopt -s histappendи да, это должно быть history -c в середине PROMPT_COMMAND) Эта версия также имеет два важных недостатка:

    1. Файл истории должен быть инициализирован. Он должен содержать хотя бы одну непустую строку (может быть что угодно).
    2. Команда historyможет дать ложный вывод - см. Ниже.

[Редактировать] "И победитель ..."

  • вариант 3:

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

    Это насколько это возможно. Это единственный вариант, когда обе erasedupsистории работают одновременно. Это , вероятно, окончательное решение всех ваших проблем, Ахан.


2. Почему вариант 2 не работает (или: что на самом деле не работает, как ожидалось)?

Как я уже говорил, каждое из приведенных выше решений работает по-своему. Но самая неверная интерпретация того, как работают настройки, связана с анализом результатов historyкоманды . Во многих случаях команда может дать ложный вывод. Почему? Потому что он выполняется перед последовательностью других historyкоманд, содержащихся в PROMPT_COMMAND! Однако при использовании второго или третьего варианта можно отслеживать изменения .bash_historyсодержимого ( watch -n1 "tail -n20 .bash_history"например, используя) и видеть, какова реальная история.

3. Почему вариант 3 такой сложный?

Все лежит в том, как erasedupsработает. Как говорится в руководстве по bash, «(...) erasedupsвызывает удаление всех предыдущих строк, соответствующих текущей строке, из списка истории перед сохранением этой строки» . Так что это действительно то, чего хотел ОП (а не просто, как я думал ранее, чтобы не было дубликатов, появляющихся последовательно ) . Вот почему каждая из history -.команд должна или не может быть в PROMPT_COMMAND:

  • history -n должен быть там, прежде чем history -wчитать из .bash_historyкоманд, сохраненных с любого другого терминала,

  • history -w имеет быть там, чтобы сохранить историю в файл и удалить дубликаты,

  • history -a не должны быть помещены туда вместо history -w, потому что это не вызывает стирание дубликатов,

  • history -cтакже необходимо, потому что это предотвращает разрушение буфера истории после каждой команды,

  • и , наконец, history -rэто необходимо для восстановления буфера истории из файла, таким образом , наконец , что делает историю совместную через терминальные сессии.


2
+1 за «Но самая вводящая в заблуждение интерпретация того, как работают настройки, исходит из анализа результатов команды истории». Я думаю, что это является ядром проблемы ОП. Отличный вывод.
Джейсонвриан

2
Я наконец понял вашу точку зрения. Под «дубликатами» вы имели в виду нечто иное, чем я. Я сосредоточился только на последовательностях одной и той же команды . Обновил мой ответ - см. «Вариант 3». Кроме того, для вашего случая, чтобы проверить, как работает история, вы должны использовать watch "tail -n 20 .bash_history"вместо tail -f .bash_history.
rozcietrzewiacz

2
Вариант 3 просто уничтожил все мои 100 000 строк истории :( (bash 3.2.25 (1) -релиз)
Фелипе Альварес

2
history -cтакже необходим, потому что он предотвращает уничтожение буфера истории после каждой команды. Почему происходит такое удаление?
Петр Доброгост

2
Ваше решение 3 на самом деле не работает. Это очень плохо и зависит от порядка входа пользователей в разные терминалы. Это часто приводит к потере команд, особенно при переключении между терминалами. Источник: опробовал.
6

8

В вашей командной строке вы используете -cпереключатель. От man bash:

-c   Очистить список истории, удалив все записи

Чтобы поделиться своей историей со всеми открытыми терминалами, вы можете использовать -n:

-n   прочитать строки истории, которые еще не были прочитаны из файла истории, в текущий список истории. Это строки, добавленные в файл истории с начала текущего сеанса bash.

Размер по умолчанию также указан в руководстве:

HISTSIZE Количество команд, которые нужно запомнить в истории команд (см. ИСТОРИЯ ниже). Значение по умолчанию составляет 500.

Чтобы сохранить многострочные команды:

Cmdhist вариант оболочки, если он включен, заставляет оболочку , чтобы попытаться сохранить каждую линию команды многострочного в одной и той же записи журнала, добавив точку с запятой , когда это необходимо для сохранения синтаксической правильности. Опция lithist shell заставляет оболочку сохранять команду со встроенными символами новой строки вместо точек с запятой.

Кроме того, вы не должны вводить команды HIST * предваряющим export- они являются переменными только для bash, а не переменными среды: HISTCONTROL=ignoredups:erasedupsдостаточно.


Итак, это export PROMPT_COMMAND="history -a; history -n; history -r; $PROMPT_COMMAND"правильно? Кроме того, знаете ли вы, как я могу заставить файл истории хранить многострочные команды как одну команду (которая по умолчанию отключена) ??
its_me

1
Да. В соответствии с приведенной выше страницей руководства shopt -s cmdhistсохранит несколько строк.
Джейсонвриан

хорошо, я попробовал их все. Похоже, HISTCONTROL=ignoredups:erasedupsне работает. Я также попытался иметь только это в .bashrcфайле (среди пользовательских функций). Есть идеи, что может быть не так? Я использую Fedora 15 на виртуальной машине - - Windows 7 host.
its_me

Убедитесь, что в других файлах запуска, например /etc/bashrc, и .bash_profileт. Д., Нет ничего, что могло бы его переопределить.
Джейсонвриан

Я не вижу никаких проблем с .bash_profileфайлом. Что касается содержания, которое /etc/bashrcя поместил здесь, пожалуйста, посмотрите - pastebin.com/Uae6sE6s
its_me

8

Это то, что я придумал, и я доволен этим до сих пор ...

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" 

ПРИМЕЧАНИЯ:

  • Да, это сложно ... но он удаляет все дубликаты и сохраняет хронологию в каждом терминале!
  • My HISTIGNOREигнорирует все команды, которые не имеют аргументов. Это может быть нежелательным для некоторых людей и может быть опущено.


0

Это не работает, потому что вы забыли о:

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

Но, кажется history -n, просто глючит, когда export HISTCONTROL=ignoreboth:erasedupsдействует.

Давайте поэкспериментируем:

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

Здесь мы включаем удаление дубликатов, переключаем историю в пользовательский файл, очищаем историю. После завершения всех команд у нас есть пустой файл истории и одна команда в текущей истории.

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

Откройте второй терминал и выполните эти шесть команд тоже. После того:

$ 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"

Теперь в вашей текущей истории есть две команды, а в файле истории:

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

Вернуться к первому терминалу:

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

Хм ... ни одна из echoкоманд не читается. Снова переключитесь на второй терминал и:

$ echo "Z"
$ history -w

Теперь файл истории:

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

Снова переключитесь на первый терминал:

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

Вы можете видеть, что echo "Z"команда объединена с history -n.

Другая ошибка связана с тем, что команды считываются из истории по номеру команды, а не по времени команды, я думаю. Я ожидаю, что другие echoкоманды появились в истории


0

erasedups не обрезает (как chomp в некоторых языках) начальные и конечные пробелы. Это ошибка. Также erasedups не удаляет все предыдущие записи.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.