it-swarm-ru.tech

Сохранить историю Bash в нескольких окнах терминала

У меня постоянно открыто более одного терминала. В любом месте от двух до десяти, делая различные биты и качки. Теперь допустим, я перезагружаюсь и открываю другой набор терминалов. Некоторые помнят определенные вещи, некоторые забывают.

Я хочу историю, которая:

  • Помнит все с каждого терминала
  • Мгновенно доступен из любого терминала (например, если I ls в одном, переключиться на другой уже работающий терминал и затем нажать вверх, появится ls)
  • Не забывает команду, если перед командой есть пробелы.

Что-нибудь, что я могу сделать, чтобы заставить bash работать так больше?

565
Oli

Добавьте следующее в ~/.bashrc

# Avoid duplicates
HISTCONTROL=ignoredups:erasedups  
# When the Shell exits, append to the history file instead of overwriting it
shopt -s histappend

# After each command, append to the history file and reread it
Prompt_COMMAND="${Prompt_COMMAND:+$Prompt_COMMAND$'\n'}history -a; history -c; history -r"
355
Pablo R.

Итак, это все, что связано с моей историей .bashrc предмет:

export HISTCONTROL=ignoredups:erasedups  # no duplicate entries
export HISTSIZE=100000                   # big big history
export HISTFILESIZE=100000               # big big history
shopt -s histappend                      # append to history, don't overwrite it

# Save and reload the history after each command finishes
export Prompt_COMMAND="history -a; history -c; history -r; $Prompt_COMMAND"

Протестировано с bash 3.2.17 на Mac OS X 10.5, bash 4.1.7 на 10.6.

257
kch

Вот моя попытка поделиться историей сессий Bash. Это позволит совместно использовать историю между сеансами bash таким образом, чтобы счетчик истории не путался, и расширение истории, такое как _!number_, будет работать (с некоторыми ограничениями).

Использование Bash версии 4.1.5 под Ubuntu 10.04 LTS (Lucid Lynx).

_HISTSIZE=9000
HISTFILESIZE=$HISTSIZE
HISTCONTROL=ignorespace:ignoredups

_bash_history_sync() {
    builtin history -a         #1
    HISTFILESIZE=$HISTSIZE     #2
    builtin history -c         #3
    builtin history -r         #4
}

history() {                  #5
    _bash_history_sync
    builtin history "[email protected]"
}

Prompt_COMMAND=_bash_history_sync
_

Объяснение:

  1. Добавьте только что введенную строку к _$HISTFILE_ (по умолчанию _.bash_history_). Это приведет к росту _$HISTFILE_ на одну строку.

  2. Установка специальной переменной _$HISTFILESIZE_ в какое-либо значение приведет к тому, что Bash усекает строки _$HISTFILE_ до длинных строк _$HISTFILESIZE_, удаляя самые старые записи.

  3. Очистить историю запущенного сеанса. Это уменьшит счетчик истории на величину _$HISTSIZE_.

  4. Прочитайте содержимое _$HISTFILE_ и вставьте их в текущую историю сеанса работы. это увеличит счетчик истории на количество строк в _$HISTFILE_. Обратите внимание, что количество строк _$HISTFILE_ не обязательно _$HISTFILESIZE_.

  5. Функция history() переопределяет встроенную историю, чтобы обеспечить синхронизацию истории перед ее отображением. Это необходимо для расширения истории по номеру (подробнее об этом позже).

Больше объяснений:

  • Шаг 1 гарантирует, что команда из текущего запущенного сеанса будет записана в файл глобальной истории.

  • Шаг 4 гарантирует, что команды из других сеансов будут считаны в текущую историю сеанса.

  • Поскольку шаг 4 увеличит счетчик истории, нам нужно каким-то образом уменьшить счетчик. Это делается на шаге 3.

  • На шаге 3 счетчик истории уменьшается на _$HISTSIZE_. На шаге 4 счетчик истории увеличивается на количество строк в _$HISTFILE_. На шаге 2 мы удостоверимся, что число строк _$HISTFILE_ точно равно _$HISTSIZE_ (это означает, что _$HISTFILESIZE_ должно совпадать с _$HISTSIZE_).

Об ограничениях расширения истории:

При использовании расширения истории по номеру, вы должны всегда искать число немедленно перед его использованием. Это означает, что bash не отображает подсказку между поиском номера и его использованием. Это обычно означает, что нет ввода и не Ctrl + C.

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

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

Обычно я использую расширение истории по номеру, как это

_$ history | grep something #note number
$ !number
_

Я рекомендую использовать следующие опции Bash.

_## reedit a history substitution line if it failed
shopt -s histreedit
## edit a recalled history line before executing
shopt -s histverify
_

Странные баги:

Выполнение команды истории, переданной по каналу, приведет к тому, что эта команда будет дважды указана в истории. Например:

_$ history | head
$ history | tail
$ history | grep foo
$ history | true
$ history | false
_

Все будет перечислено в истории дважды. Понятия не имею почему.

Идеи для улучшения:

  • Измените функцию _bash_history_sync(), чтобы она не выполнялась каждый раз. Например, он не должен выполняться после _CTRL+C_ в приглашении. Я часто использую _CTRL+C_, чтобы отбросить длинную командную строку, когда решаю, что не хочу выполнять эту строку. Иногда мне приходится использовать _CTRL+C_, чтобы остановить скрипт завершения Bash.

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

123
lesmana

Я не знаю ни одного способа использования bash. Но это одна из самых популярных функций zsh .
Лично я предпочитаю zsh, а не bash, поэтому рекомендую попробовать.

Вот часть моего .zshrc, которая имеет дело с историей:

SAVEHIST=10000 # Number of entries
HISTSIZE=10000
HISTFILE=~/.zsh/history # File
setopt APPEND_HISTORY # Don't erase history
setopt EXTENDED_HISTORY # Add additional data to history like timestamp
setopt INC_APPEND_HISTORY # Add immediately
setopt HIST_FIND_NO_DUPS # Don't show duplicates in search
setopt HIST_IGNORE_SPACE # Don't preserve spaces. You may want to turn it off
setopt NO_HIST_BEEP # Don't beep
setopt SHARE_HISTORY # Share history between session/terminals
45
Maciej Piechotka

Для этого вам нужно добавить две строки в ваш ~/.bashrc:

shopt -s histappend
Prompt_COMMAND="history -a;history -c;history -r;$Prompt_COMMAND"

От man bash:

Если опция оболочки Histappend включена (см. Описание shopt в разделе "Команды сборки" ниже), строки добавляются в файл истории, в противном случае файл истории перезаписывается.

17
Chris Down

Вы можете отредактировать вашу подсказку BASH для запуска "history -a" и "history -r", которые предложил Muerr:

savePS1=$PS1

(если вы что-то испортили, что почти гарантировано)

PS1=$savePS1`history -a;history -r`

(обратите внимание, что это обратные тики; они будут запускать history -a и history -r в каждой подсказке. Поскольку они не выводят никакого текста, ваша подсказка не изменится.

После того как переменная PS1 настроена так, как вы хотите, установите ее в файле ~/.bashrc навсегда.

Если вы хотите вернуться к своей первоначальной подсказке во время тестирования, выполните:

PS1=$savePS1

Я провел базовое тестирование, чтобы убедиться, что оно работает, но не могу говорить о каких-либо побочных эффектах от запуска history -a;history -r в каждой строке.

11
Schof

Если вам нужно решение для синхронизации истории bash или zsh, которое также решает проблему, приведенную ниже, обратитесь к ней по адресу http://ptspts.blogspot.com/2011/03/how-to-automatics-synchronize-Shell.html

Проблема заключается в следующем: у меня есть два окна Shell A и B. В окне Shell A я запускаю sleep 9999 и ​​(не дожидаясь окончания сна) в окне оболочки B я хочу видеть sleep 9999 в истории bash.

Причина, по которой большинство других решений здесь не решают эту проблему, заключается в том, что они записывают свои изменения истории в файл истории, используя Prompt_COMMAND или PS1, оба из которых выполняются слишком поздно, только после sleep 9999 команда выполнена.

10
pts

Итак, в конце концов это раздражало меня, чтобы найти достойное решение:

# Write history after each command
_bash_history_append() {
    builtin history -a
}
Prompt_COMMAND="_bash_history_append; $Prompt_COMMAND"

То, что это делает, является своего рода объединением того, что было сказано в этой теме, за исключением того, что я не понимаю, зачем вам перезагружать глобальную историю после каждой команды. Меня очень мало волнует, что происходит в других терминалах, но я всегда запускаю серию команд, скажем, в одном терминале:

make
ls -lh target/*.foo
scp target/artifact.foo vm:~/

(Упрощенный пример)

И в другом:

pv ~/test.data | nc vm:5000 >> output
less output
mv output output.backup1

Ни за что бы я не хотел, чтобы команда была передана

9
Yarek T

Ты можешь использовать history -a, чтобы добавить историю текущего сеанса в исторический файл, затем используйте history -r на других терминалах, чтобы прочитать историю.

9
jtimberman

Вот альтернатива, которую я использую. Это громоздко, но решает проблему, о которой упоминал @axel_c, когда иногда вы можете захотеть иметь отдельный экземпляр истории в каждом терминале (один для make, один для мониторинга, один для vim и т.д.).

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

history | grep -v history >> ~/master_history.txt

Это добавляет всю историю из текущего терминала в файл с именем master_history.txt в вашем домашнем каталоге.

У меня также есть отдельная горячая клавиша для поиска в главном файле истории:

cat /home/toby/master_history.txt | grep -i

Я использую кошку | grep, потому что он оставляет курсор в конце, чтобы ввести мое регулярное выражение. Менее уродливый способ сделать это - добавить пару скриптов на ваш путь для выполнения этих задач, но горячие клавиши работают для моих целей. Я также периодически извлекаю историю из других хостов, над которыми я работал, и добавляю эту историю в мой файл master_history.txt.

Всегда приятно иметь возможность быстро найти и найти то хитрое регулярное выражение, которое вы использовали, или ту странную версию Perl, которую вы нашли 7 месяцев назад.

8
Toby

Я могу предложить исправление для этого последнего: убедитесь, что переменная env HISTCONTROL не определяет "ignorespace" (или "ignoreboth").

Но я чувствую твою боль от нескольких одновременных сеансов. Это просто плохо обрабатывается в bash.

7
jmanning2k

Вот мое усовершенствование к @ lesmana's ответ . Основное отличие состоит в том, что параллельные окна не делятся историей. Это означает, что вы можете продолжать работать в своих окнах, не загружая контекст из других окон в ваши текущие окна.

Если вы явно введете "history", OR если вы откроете новое окно, то вы получите историю из всех предыдущих окон.

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

# Consistent and forever bash history
HISTSIZE=100000
HISTFILESIZE=$HISTSIZE
HISTCONTROL=ignorespace:ignoredups

_bash_history_sync() {
  builtin history -a         #1
  HISTFILESIZE=$HISTSIZE     #2
}

_bash_history_sync_and_reload() {
  builtin history -a         #1
  HISTFILESIZE=$HISTSIZE     #2
  builtin history -c         #3
  builtin history -r         #4
}

history() {                  #5
  _bash_history_sync_and_reload
  builtin history "[email protected]"
}

export HISTTIMEFORMAT="%y/%m/%d %H:%M:%S   "
Prompt_COMMAND='history 1 >> ${HOME}/.bash_eternal_history'
Prompt_COMMAND=_bash_history_sync;$Prompt_COMMAND
7
rouble

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

# Convert /dev/nnn/X or /dev/nnnX to "nnnX"
HISTSUFFIX=`tty | sed 's/\///g;s/^dev//g'`
# History file is now .bash_history_pts0
HISTFILE=".bash_history_$HISTSUFFIX"
HISTTIMEFORMAT="%y-%m-%d %H:%M:%S "
HISTCONTROL=ignoredups:ignorespace
shopt -s histappend
HISTSIZE=1000
HISTFILESIZE=5000

История теперь выглядит так:

[email protected]:~# test 123
[email protected]:~# test 5451
[email protected]:~# history
1  15-08-11 10:09:58 test 123
2  15-08-11 10:10:00 test 5451
3  15-08-11 10:10:02 history

С файлами, похожими на:

[email protected]:~# ls -la .bash*
-rw------- 1 root root  4275 Aug 11 09:42 .bash_history_pts0
-rw------- 1 root root    75 Aug 11 09:49 .bash_history_pts1
-rw-r--r-- 1 root root  3120 Aug 11 10:09 .bashrc
6
Litch

Здесь я укажу одну проблему с

export Prompt_COMMAND="${Prompt_COMMAND:+$Prompt_COMMAND$'\n'}history -a; history -c; history -r"

а также

Prompt_COMMAND="$Prompt_COMMAND;history -a; history -n"

Если вы запустите source ~/.bashrc, $ Prompt_COMMAND будет выглядеть так:

"history -a; history -c; history -r history -a; history -c; history -r"

а также

"history -a; history -n history -a; history -n"

Это повторение происходит каждый раз, когда вы запускаете 'source ~/.bashrc'. Вы можете проверить Prompt_COMMAND после каждого запуска 'source ~/.bashrc', запустив 'echo $ Prompt_COMMAND'.

Вы могли видеть, что некоторые команды явно не работают: "history -n history -a". Но хорошая новость заключается в том, что это все еще работает, потому что другие части все еще формируют правильную последовательность команд (просто влечет за собой некоторые дополнительные расходы из-за повторного выполнения некоторых команд. И не настолько чистые.)

Лично я использую следующую простую версию:

shopt -s histappend
Prompt_COMMAND="history -a; history -c; history -r"

который имеет большинство функциональных возможностей, в то время как нет такой проблемы, как указано выше.

Еще одно замечание: в действительности нет ничего волшебного . Prompt_COMMAND - это просто переменная окружения bash. Команды в нем выполняются до того, как вы получите bash Prompt (знак $). Например, ваш Prompt_COMMAND имеет значение "echo 123", и вы запускаете "ls" в своем терминале. Эффект подобен запуску "ls; echo 123".

$ Prompt_COMMAND="echo 123"

вывод (точно так же, как при запуске 'Prompt_COMMAND = "echo 123"; $ Prompt_COMMAND'):

123

Запустите следующее:

$ echo 3

вывод:

3
123

"history -a" используется для записи команд истории в памяти в ~/.bash_history

"history -c" используется для очистки истории команд в памяти

"history -r" используется для чтения команд истории из ~/.bash_history в память

См. Объяснение команды истории здесь: http://ss64.com/bash/history.html

PS: Как отмечали другие пользователи, экспорт не нужен. Смотрите: используя экспорт в .bashrc

5
fstang

Я написал скрипт для установки файла истории для каждой сессии или задачи, основанной на следующем.

        # write existing history to the old file
        history -a

        # set new historyfile
        export HISTFILE="$1"
        export HISET=$1

        # touch the new file to make sure it exists
        touch $HISTFILE
        # load new history file
        history -r $HISTFILE

Не обязательно сохранять каждую команду истории, но она сохраняет те, которые меня интересуют, и их легче найти, затем выполнить каждую команду. Моя версия также перечисляет все файлы истории и предоставляет возможность поиска по всем.

Полный источник: https://github.com/simotek/scripts-config/blob/master/hiset.sh

3
simotek

Вот решение, которое не не смешивает истории отдельных сессий!

В основном нужно хранить историю каждой сессии отдельно и воссоздавать ее в каждом приглашении. Да, он использует больше ресурсов, но это не так медленно, как может показаться - задержка становится заметной, только если у вас есть более 100000 записей истории.

Вот основная логика:

# on every Prompt, save new history to dedicated file and recreate full history
# by reading all files, always keeping history from current session on top.
update_history () {
  history -a ${HISTFILE}.$$
  history -c
  history -r
  for f in `ls ${HISTFILE}.[0-9]* | grep -v "${HISTFILE}.$$\$"`; do
    history -r $f
  done
  history -r "${HISTFILE}.$$"
}
export Prompt_COMMAND='update_history'

# merge session history into main history file on bash exit
merge_session_history () {
  cat ${HISTFILE}.$$ >> $HISTFILE
  rm ${HISTFILE}.$$
}
trap merge_session_history EXIT

Смотрите это Gist для полного решения, включая некоторые меры безопасности и оптимизации производительности.

3
Jan Warchoł

Потому что я предпочитаю бесконечную историю, которая сохраняется в пользовательском файле. Я создаю эту конфигурацию на основе https://stackoverflow.com/a/19533853/4632019 :

export HISTFILESIZE=
export HISTSIZE=
export HISTTIMEFORMAT="[%F %T] "

export HISTFILE=~/.bash_myhistory
Prompt_COMMAND="history -a; history -r; $Prompt_COMMAND"
2
Eugen Konkov

Это работает для ZSH

##############################################################################
# History Configuration for ZSH
##############################################################################
HISTSIZE=10000               #How many lines of history to keep in memory
HISTFILE=~/.zsh_history     #Where to save history to disk
SAVEHIST=10000               #Number of history entries to save to disk
#HISTDUP=erase               #Erase duplicates in the history file
setopt    appendhistory     #Append history to the history file (no overwriting)
setopt    sharehistory      #Share history across terminals
setopt    incappendhistory  #Immediately append to the history file, not just when a term is killed
2
Mulki

Я давно хотел этого, особенно возможность получить команду, по которой она была выполнена, для повторного выполнения в новом проекте (или найти каталог по команде). Поэтому я собрал этот инструмент , который сочетает в себе предыдущие решения для хранения глобальной истории CLI с интерактивным инструментом поиска по имени percol (сопоставлен с C ^ R). Это все еще замечательно на первой машине, которую я начал использовать, теперь с> 2-летней историей CLI.

Он не вмешивается в локальную историю CLI, поскольку это касается клавиш со стрелками, но позволяет довольно легко получить доступ к глобальной истории (которую можно также сопоставить с чем-то другим, кроме C ^ R)

2
Gordon Wells

Вот фрагмент из моего .bashrc и короткие пояснения, где это необходимо:

# The following line ensures that history logs screen commands as well
shopt -s histappend

# This line makes the history file to be rewritten and reread at each bash Prompt
PROMPT_COMMAND="$Prompt_COMMAND;history -a; history -n"
# Have lots of history
HISTSIZE=100000         # remember the last 100000 commands
HISTFILESIZE=100000     # start truncating commands after 100000 lines
HISTCONTROL=ignoreboth  # ignoreboth is shorthand for ignorespace and     ignoredups

HISTFILESIZE и HISTSIZE являются личными предпочтениями, и вы можете изменить их по своему вкусу.

0
Hopping Bunny