Сохранять историю bash в нескольких терминальных окнах
У меня последовательно открыто более одного терминала. Везде от двух до десяти, выполняя различные бит и бобы. Теперь предположим, что я перезапускаю и открываю еще один набор терминалов. Некоторые помнят определенные вещи, некоторые забывают.
Мне нужна история, которая:
- Помнит все из каждого терминала
- Доступно мгновенно из каждого терминала (например, если I
ls
), переключитесь на другой уже запущенный терминал и затем нажмите вверх, появитсяls
) - Не забывайте команду, если есть пробелы в начале команды.
Все, что я могу сделать, чтобы сделать bash более похожим на это?
20 ответов
Добавьте следующее в ~ /.bashrc
# Avoid duplicates
export 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
export PROMPT_COMMAND="${PROMPT_COMMAND:+$PROMPT_COMMAND$'\n'}history -a; history -c; history -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.
Вот моя попытка совместного использования истории сеансов 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
Объяснение:
-
Добавить только что введенную строку в
$HISTFILE
(по умолчанию -.bash_history
). Это приведет к тому, что$HISTFILE
будет расти на одну строку. -
Установка специальной переменной
$HISTFILESIZE
для некоторого значения приведет к тому, что Bash обрезает$HISTFILE
не более$HISTFILESIZE
строк, удалив самые старые записи. -
Очистите историю выполняемого сеанса. Это уменьшит счетчик истории на количество
$HISTSIZE
. -
Прочитайте содержимое
$HISTFILE
и вставьте их в текущую текущую историю сеансов. это увеличит счетчик истории на количество строк в$HISTFILE
. Обратите внимание, что количество строк$HISTFILE
не обязательно$HISTFILESIZE
. -
Функция
history()
переопределяет встроенную историю, чтобы убедиться, что история синхронизирована до ее отображения. Это необходимо для расширения истории по номеру (подробнее об этом позже).
Дополнительные пояснения:
-
Шаг 1 гарантирует, что команда из текущего текущего сеанса будет записана в глобальный файл истории.
-
Шаг 4 гарантирует, что команды из других сеансов будут прочитаны в текущей истории сеанса.
-
Поскольку шаг 4 поднимет счетчик истории, нам нужно каким-то образом уменьшить счетчик. Это делается на шаге 3.
-
На шаге 3 счетчик истории уменьшается на
$HISTSIZE
. На шаге 4 счетчик истории увеличивается числом строк в$HISTFILE
. На шаге 2 мы убеждаемся, что количество строк$HISTFILE
равно точно$HISTSIZE
(это означает, что$HISTFILESIZE
должен быть таким же, как $ HISTSIZE).
Об ограничениях расширения истории:
При использовании расширения истории по номеру, вы должны всегда находить число сразу перед его использованием. Это означает, что нет подсказки подсказки bash между просмотром номера и использованием его. Обычно это означает, что нет ввода и нет ctrl + c.
Как правило, после того, как у вас есть более одного сеанса Bash, нет никакой гарантии, что расширение истории по числу будет сохранять свое значение между двумя отображаемыми подсказками Bash. Потому что, когда выполняется $HISTSIZE
, история из всех других сессий Bash интегрируется в историю текущего сеанса. Если какой-либо другой сеанс bash имеет новую команду, тогда номера истории текущего сеанса будут разными.
Я считаю это ограничение разумным. Я все равно должен смотреть номер каждый раз, потому что я не могу запомнить произвольные номера истории.
Обычно я использую расширение истории по числу, подобному этому
PROMPT_COMMAND
Я рекомендую использовать следующие параметры Bash.
$ history | grep something #note number
$ !number
Странные ошибки:
Запуск команды history, переданной на все, приведет к тому, что команда будет дважды указана в истории. Например:
## 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
, чтобы остановить скрипт завершения Bash. -
Команды из текущего сеанса всегда должны быть самыми последними в истории текущего сеанса. Это также будет иметь побочный эффект, что данный номер истории сохраняет свое значение для записей истории из этого сеанса.
Я не знаю, как использовать bash
. Но это одна из самых популярных функций zsh
.
Лично я предпочитаю zsh
over 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
Для этого вам нужно добавить две строки в ваш ~/.bashrc
:
shopt -s histappend
PROMPT_COMMAND="history -a;history -c;history -r;$PROMPT_COMMAND"
Из man bash
:
Если включена опция оболочки histappend (см. описание магазина в разделе SHELL BUILTIN COMMANDS ниже), строки добавляются в файл истории, в противном случае файл истории переписан.
Вы можете отредактировать свое приглашение BASH для запуска «истории -a» и «истории -r», предложенной Мурером:
savePS1=$PS1
(в случае, если вам что-то не хватает, что почти гарантировано)
PS1=$savePS1`history -a;history -r`
(обратите внимание, что это обратные тики, они будут запускать историю -a и history -r в каждом приглашении. Поскольку они не выводят никакого текста, ваше приглашение не изменится.
После того, как ваша переменная PS1 настроена так, как вы хотите, установите ее навсегда в файле ~ /.bashrc.
Если вы хотите вернуться к исходному приглашению во время тестирования, выполните следующие действия:
PS1=$savePS1
Я провел базовое тестирование на этом, чтобы убедиться, что он работает, но не может говорить о каких-либо побочных эффектах от запуска history -a;history -r
в каждом приглашении.
Если вам требуется синхронизирующее решение истории bash или zsh, которое также решает проблему ниже, а затем посмотрите на http://ptspts.blogspot.com/2011/03/how-to-automatically-synchronize-shell.html
Проблема заключается в следующем: у меня есть два окна оболочки A и B. В окне оболочки A я запускаю sleep 9999
и (не дожидаясь завершения сна) в окне оболочки B, Я хочу видеть sleep 9999
в истории bash.
Причина, по которой большинство других решений здесь не решает эту проблему, заключается в том, что они записывают свои исторические изменения в файл истории, используя PROMPT_COMMAND
или PS1
, оба которые выполняются слишком поздно, только после завершения команды sleep 9999
.
Вы можете использовать историю history -a
, чтобы добавить историю текущего сеанса в файл hist, а затем использовать history -r
на других терминалах для чтения файла hist.Â
Вот альтернатива, которую я использую. Это громоздко, но в нем рассматривается проблема, о которой @axel_c упоминает, где иногда вам может понадобиться отдельный экземпляр истории в каждом терминале (один для make, один для мониторинга, один для vim и т. Д.).
Я сохраняю отдельный файл прикрепленных файлов, который я постоянно обновляю. У меня есть следующее сопоставление с горячей клавишей:
history | grep -v history >> ~/master_history.txt
Это добавляет всю историю из текущего терминала в файл с именем master_history.txt в вашем домашнем каталоге.
У меня также есть отдельная горячая клавиша для поиска по основному файлу истории:
cat /home/toby/master_history.txt | grep -i
Я использую cat | grep, потому что он оставляет курсор в конце, чтобы ввести мое регулярное выражение. Менее уродливым способом сделать это было бы добавить несколько сценариев на ваш путь для выполнения этих задач, но горячие клавиши работают для моих целей. Я также периодически вытягиваю историю с других хостов, над которыми я работал, и добавляю эту историю в свой файл master_history.txt.
Всегда приятно быстро находить и находить это сложное регулярное выражение, которое вы использовали, или этот странный perl one-liner, который вы создали 7 месяцев назад.
Я могу предложить исправление для последнего: убедитесь, что переменная env HISTCONTROL не указывает «ignorespace» (или «ignoreboth»).
Но я чувствую вашу боль с несколькими параллельными сеансами. Это просто плохо обрабатывается в bash.
Правильно. Поэтому, наконец, это раздражало меня, чтобы найти достойное решение:
# 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
Никоим образом я не хочу, чтобы команда была разделяемой
Вот мое улучшение для ответа @ lesmana . Основное различие заключается в том, что параллельные окна не обмениваются историей. Это означает, что вы можете продолжать работать в своих окнах, не имея контекста из других окон, загружаемых в ваши текущие окна.
Если вы явно вводите «историю», ИЛИ, если вы открываете новое окно, вы получаете историю из всех предыдущих окон.
Кроме того, я использую эту стратегию для архивирования каждой команды, когда-либо введенной на моей машине .
# 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
Я написал сценарий для установки файла истории для каждого сеанса или задачи, основываясь на следующем.
# 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
Я решил поместить историю в файл-за-tty, так как несколько человек могут работать на одном сервере - разделение команд каждого сеанса упрощает аудит.
# 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
Здесь я укажу одну проблему с
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_COMMAND является «echo 123», и вы запускаете «ls» в своем терминале. Эффект подобен запуску «ls; echo 123».
$ PROMPT_COMMAND="echo 123"
output (Так же, как запуск: 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
Вот фрагмент из моего .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 являются личными предпочтениями, и вы можете изменить их в соответствии с вашими вкусами.
Это работает для 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
Я давно хотел этого, особенно возможность получить команду, где она была запущена для повторного выполнения в новом проекте (или найти каталог по команде). Поэтому я добавил этот инструмент вместе с предыдущими решениями для хранения глобальной истории CLI с помощью интерактивного инструмента grepping называемый перколом (отображается на C ^ R). Это все еще пятно на первой машине, которую я начал использовать, теперь с историей CLI 2 года.
Это не связано с локальной историей CLI в отношении клавиш со стрелками, но позволяет вам легко получить доступ к глобальной истории (что также можно сопоставить с чем-то другим, кроме C ^ R)
Вот решение, в котором не смешивает истории с отдельных сеансов!
В основном нужно хранить историю каждой сессии отдельно и воссоздавать ее в каждом приглашении. Да, он использует больше ресурсов, но это не так медленно, как может показаться - задержка начинает заметна, только если у вас более 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
См. этот метод для полного решения, включая некоторые меры предосторожности и оптимизацию производительности.
Потому что я предпочитаю бесконечную историю, которая сохраняется в пользовательском файле. Я создаю эту конфигурацию на основе 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"