it-swarm.cn

在多个终端窗口中保留bash历史记录

我一直在打开多个终端。从2到10的任何位置,进行各种操作。现在,我们重新启动并打开另一组终端。有些人记得某些事情,有些人忘记了。

我想要一段历史:

  • 记住每个终端的一切
  • 可以从每个终端即时访问(例如,如果我在一个终端中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"

在Mac OS X 10.5上使用bash 3.2.17进行了测试,在10.6上使用bash 4.1.7进行了测试。

257
kch

这是我尝试的Bash会话历史记录共享。这将使bash会话之间的历史记录共享成为可能,从而不会混淆历史记录计数器,并且像_!number_这样的历史记录扩展会起作用(有一些约束)。

在Ubuntu 10.04 LTS(Lucid Lynx)下使用Bash版本4.1.5。

_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 Shell选项(请参见下面的Shell BUILTIN COMMANDS下的shopt的说明),则这些行将附加到历史文件中,否则历史文件将被覆盖。

17
Chris Down

您可以编辑BASH提示符以运行Muerr建议的“ history -a”和“ history -r”:

savePS1=$PS1

(以防万一,几乎可以保证)

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

(请注意,这些是反引号;它们将在每个提示上运行历史记录-a和历史记录-r。由于它们不输出任何文本,因此您的提示将保持不变。

一旦按照您的方式设置了PS1变量,就将其永久设置为〜/ .bashrc文件。

如果您想在测试时返回到原始提示,请执行以下操作:

PS1=$savePS1

我已经对此进行了基本测试,以确保它能正常工作,但是不能对在每个提示上运行history -a;history -r产生任何副作用。

11
Schof

如果您需要同时解决以下问题的bash或zsh历史记录同步解决方案,请在 http://ptspts.blogspot.com/2011/03/how-to-automatically-synchronize-Shell.html

问题如下:我有两个Shell窗口A和B。在Shell窗口A中,我运行sleep 9999,并且在Shell窗口B中(无需等待睡眠完成),我希望能够看到bash历史记录中的sleep 9999

此处大多数其他解决方案无法解决此问题的原因是,它们使用Prompt_COMMANDPS1将其历史更改写入历史文件,这两种方法都执行得太晚了,只是在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文件中。

能够快速搜索并找到您使用的棘手的正则表达式或7个月前提出的怪异的Perl单行代码总是很高兴。

8
Toby

我可以为最后一个提供修复程序:确保环境变量HISTCONTROL不指定“ ignorespace”(或“ ignoreboth”)。

但是我感到您同时进行多个会话感到很痛苦。用bash处理起来根本不好。

7
jmanning2k

这是我对@lesmana的 answer 的增强。主要区别在于并发窗口不共享历史记录。这意味着您可以继续在Windows中工作,而不会将其他窗口的上下文加载到当前窗口中。

如果您显式键入'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"

如果运行源〜/ .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”时,都会发生此重复。您可以在每次运行“ source〜/ .bashrc”后通过运行“ echo $ Prompt_COMMAND”来检查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"

输出(就像运行'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:正如其他用户指出的那样,不需要export。请参阅: 在.bashrc中使用export

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

有关完整的解决方案,请参阅 此要点 ,其中包括一些防护措施和性能优化。

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)的交互式grepping工具结合在一起。在我开始使用它的第一台计算机上,它仍然很光滑,现在具有超过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