it-swarm-ru.tech

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

Прежде всего, это не дубликат каких-либо существующих потоков в SE. Я читал эти две темы ( 1st , 2nd ) о лучшей истории 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 (приоритет):

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

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

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. Во многих случаях команда может дать false вывод. Почему? Потому что выполняется перед последовательностью других команд 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 is необходимо для восстановления буфера истории из файла, что, в конечном итоге, делает историю общей для всех сеансов терминала.

126
rozcietrzewiacz

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

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

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

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

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

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

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

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

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

8
jasonwryan

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

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 игнорирует все команды, которые не имеют аргументов. Это может быть нежелательным для некоторых людей и может быть опущено.
8
user41176

Используйте это вместо:

HISTCONTROL=ignoreboth
1
verndog

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

 -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
Eugen Konkov

Я пошел с комбинацией ответов здесь:

  • Я хочу, чтобы моя история была объединена в конце, поэтому я использовал функцию trap.
  • Я также хочу, чтобы мои файлы истории были разделены хостами, поэтому я изменил HISTFILE, чтобы он указывал на каталог и именованные файлы в зависимости от имени хоста.
  • Я добавил опцию игнорирования, чтобы я мог вводить пароли в виде открытого текста и не отображать их в истории, например: $ ./.secretfunction -user myuser -password mypassword не будет сохранено, поскольку начинается с пробела.
  • Я добавил history* и ​​exit для HISTIGNORE просто для того, чтобы сохранить эти команды, включая что-либо в прошлом, например history | grep awesomecommand оттуда, так как я обычно не хотел бы этого. Вы можете добавить другие, такие как: выход из системы, FG, BG и т.д.
  • Затем я помещаю все это в файл ~/.bash_aliases, чтобы он не конфликтовал с обновлениями, как это иногда случалось.
HISTCONTROL=ignoreboth:erasedups:ignorespace
HISTIGNORE="history*:exit"
HISTFILE=~/.bash_history/.$(hostname)_history
shopt -s histappend
function historymerge {
    history -n; history -w; history -c; history -r;
}
trap historymerge EXIT
Prompt_COMMAND="history -a; $Prompt_COMMAND"

Grep ваш ~/для Prompt_COMMAND, чтобы убедиться, что вы не модифицируете его с помощью history -a. Я также добавил формат ВРЕМЯ/ДАТА HISTTIMEFORMAT="%x %r %Z " Мне понравилось, но я не включил в вышеупомянутый блок, так как он чувствителен к локали.

0
Marlon

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

0
Sajith Nallithodi

Использование комбинации опции 3 @ rozcietrzewiacz с ловушкой выхода позволит терминалам, которые поддерживают свои собственные независимые сеансы истории, которые сходятся при закрытии. Кажется, он даже хорошо работает с несколькими сеансами на разных компьютерах, использующих удаленный домашний каталог.

export HISTSIZE=5000
export HISTFILESIZE=5000
export HISTCONTROL=ignorespace:erasedups
shopt -s histappend
function historymerge {
    history -n; history -w; history -c; history -r;
}
trap historymerge EXIT
Prompt_COMMAND="history -a; $Prompt_COMMAND"
  • Функция historymerge загружает строки вне сеанса из файла истории, объединяет их с историей сеанса, записывает новый файл истории с дедупликацией (таким образом, стирая все ранее добавленные дубликаты строк), и перезагружает историю.

  • Сохраняя history -a в приглашении минимизирует вероятность потери истории, поскольку обновляет файл истории без дедупликации для каждой команды.

  • Наконец, ловушка запускает historymerge при закрытии сеанса для обновленного файла чистой истории с последними командами закрытого сеанса, всплывающими в конец файла (я думаю).

При этом каждый сеанс терминала будет иметь свою собственную независимую историю, начиная с запуска. Я нахожу это более полезным, так как у меня разные терминалы открыты для разных задач (поэтому я хочу повторять разные команды в каждой) Это также, к счастью, проще, чем осмысление того, что несколько терминалов постоянно пытаются повторно синхронизировать их упорядоченную историю распределенным способом (хотя вы все равно можете сделать это сознательно, используя функцию historymerge с выбранными сеансами или все они).

Обратите внимание, что вы хотите, чтобы ограничения размера были достаточно большими, чтобы вместить желаемую длину истории плюс объем недедуплицированных строк, которые могут добавляться всеми одновременно активными сеансами. 5000 достаточно для меня во многом благодаря широкому использованию HISTIGNORE для фильтрации спамовых команд с низким значением.

0
HonoredMule