bash history 命令常见使用方法
2013-04-07 12:04:04 阿炯

Bash中的'history'指令在系统管理员日常使用中比较常见,它能提供当前及以前执行过的指令,可以使用找到之前执行过指令或追查一些问题等,本文将介绍一些关于它的使用方法和技巧,以提升在命令操作时的效率。

相关变量

变量 作用
HISTFILE 指定历史命令保存的文件路径
HISTSIZE 限制当前会话内存中保留的历史命令条数
HISTFILESIZE 限制历史文件中保存的最大命令行数
HISTCONTROL 控制哪些命令不记录(如重复/空格开头)
HISTTIMEFORMAT 设置历史命令显示的时间戳格式

快速参考表

用途 命令/配置
查看历史文件 echo $HISTFILE
修改历史文件 export HISTFILE=~/.xxx
增加历史数量 export HISTSIZE=10000
禁用历史记录 export HISTFILE=/dev/null
忽略敏感命令 export HISTIGNORE="*password*"
实时同步历史 export PROMPT_COMMAND="history -a; history -n"
显示时间戳 export HISTTIMEFORMAT="%F %T "
清空历史 history -c
写入文件 history -w
读取文件 history -r


使用 HISTTIMEFORMAT 显示时间戳

当从命令行执行 history 命令后,通常只会显示已执行命令的序号和命令本身。如果想要查看命令历史的时间戳,那么可以执行:
# export HISTTIMEFORMAT='%F %T '
# history
...
510  2013-04-07 09:49:39 w
511  2013-04-07 09:49:45 df -h
512  2013-04-07 09:49:47 perl -v
513  2013-04-07 09:49:49 history

注意:这个功能只能用在当 HISTTIMEFORMAT 这个环境变量被设置之后,之后的那些新执行的 bash 命令才会被打上正确的时间戳。在此之前的所有命令,都将会显示成设置 HISTTIMEFORMAT 变量的时间。

执行历史命令
快速重复执行上一条命令

目前有四种方法可以重复执行上一条命令:
 使用上方向键,并回车执行
 按 !! 并回车执行
 输入 !-1 并回车执行
 按 Ctrl+P 并回车执行
 
通过指定关键字来执行以前的命令

在下面的例子,输入 !perl 并回车,将执行以最近的 perl 打头的命令。

从历史命令中执行一个指定行的命令

在下面的例子中,如果你想重复执行第 5 条命令,那么可以执行 !5。

使用 Ctrl+R 搜索历史

Ctrl+R 是一个快捷键。此快捷键可以对命令历史进行搜索,对于想要重复执行某个命令的时候非常有用。当找到命令后,通常再按回车键就可以执行该命令。如果想对找到的命令进行调整后再执行,则可以按一下左或右方向键。

使用 HISTSIZE 控制历史命令记录的总行数

将下面两行内容追加到 .bash_profile 文件并重新登录 bash shell,命令历史的记录数将变成 1000 条:
# vim ~/.bash_profile
HISTSIZE=1000
HISTFILESIZE=1000

如果你想禁用 history,可以将 HISTSIZE 设置为'0':
# export HISTSIZE=0
# history
# [Note that history did not display anything]

使用 HISTFILE 更改历史文件名称

默认情况下,命令历史存储在 ~/.bashhistory 文件中。添加下列内容到 .bashprofile 文件并重新登录 bash shell,将使用 .cmd_freeoa 来存储命令历史:

# vi ~/.bash_profile
HISTFILE=/root/.cmd_freeoa

使用 HISTCONTROL 从命令历史中剔除连续重复的条目

在下面的例子中,w 命令被连续执行了三次。执行 history 后你会看到三条重复的条目。要剔除这些重复的条目,你可以将 HISTCONTROL 设置为 ignoredups:

# history | tail -4
44  w
45  w
46  w [Note that there are three w commands in history, after executing w 3 times as shown above]
47  history | tail -4
# export HISTCONTROL=ignoredups
# w
# w
# w
# history | tail -3
56  export HISTCONTROL=ignoredups
57  w [Note that there is only one w command in the history, even after executing w 3 times as shown above]
58  history | tail -4

使用 HISTCONTROL 对命令历史进行去重

上例中的 ignoredups 只能剔除连续的重复条目。要清除整个命令历史中的重复条目,可以将 HISTCONTROL 设置成 erasedups:

# export HISTCONTROL=erasedups
# w
# service httpd stop
# history | tail -3
38  w
39  service httpd stop
40  history | tail -3
# ls -ltr
# service httpd stop
# history | tail -6
35  export HISTCONTROL=erasedups
36  w
37  history | tail -3
38  ls -ltr
39  service httpd stop
[Note that the previous service httpd stop after w got erased]
40  history | tail -6

使用 HISTCONTROL 强制 history 不记住特定的命令

将 HISTCONTROL 设置为 ignorespace,并在不想被记住的命令前面输入一个空格:

# export HISTCONTROL=ignorespace
# ls -ltr
# w
#  service httpd stop [Note that there is a space at the beginning of service, to ignore this command from history]
# history | tail -3
67  ls -ltr
68  w
69  history | tail -3

使用 HISTIGNORE 忽略历史中的特定命令

下面的例子,将忽略 pwd、ls、ls -ltr 等命令:
# export HISTIGNORE="pwd:ls:ls -ltr:"
# pwd
# ls
# ls -ltr
# history | tail -3
79  export HISTIGNORE=”pwd:ls:ls -ltr:”
80  service httpd stop
81  history
[Note that history did not record pwd, ls and ls -ltr]

使用'-c'选项清空所有的命令历史

如果你想清空所有的命令历史,可以执行:
# history -c

命令替换

在下面的例子里,!!:$ 将为当前的命令获得上一条命令的参数:

# ls httpd.conf
httpd.conf
# vim !$
vim httpd.conf

下例中,!^ 从上一条命令获得第一项参数:

# cp httpd.conf httpd.conf.bak
httpd.conf
# vim -5 !^
vim httpd.conf

为特定的命令替换指定的参数

在下面的例子,!cp:2 从命令历史中搜索以 cp 开头的命令,并获取它的第二项参数:
# cp lost.log /tmp/op.log
# stat !cp:2
stat /tmp/op.log

下例里,!cp:$ 获取 cp 命令的最后一项参数:

# ls -l !cp:$
ls -lh /tmp/op.log

多个会话同时执行命令后history记录不全的问题

问题场景

linux默认配置是当打开一个shell终端后,执行的所有命令均不会写入到~/.bash_history文件中,只有当前用户退出后才会写入,这期间发生的所有命令其它终端是感知不到的情况下。那么问题来了,假若之前history命令记录为c0,用户先打开了shell终端a执行了一部分命令c1,又打开了一个shell终端b,又执行了一部分命令c2。

问题1、终端a执行的这部分命令终端b上看不到。

问题2、终端a正常退出,相关命令会写入到~/.bash_history文件中(c1命令也会写入,即c0+c1),等到终端b正常退出后,相关命令也会写入到~/.bash_history文件中,注意这个时候终端b写入的内容为c0+c2,也即c1记录会丢失!!!

解决方案

将下面这段内容添加到~/.bashrc 并执行它即可
# format history
# save in ~/.bashrc
USERIP=`who -u am i 2>/dev/null| awk '{print $NF}'|sed -e 's/[()]//g'`
export HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S  `whoami`@${USER_IP}: "
export HISTFILESIZE=10000
export PROMPT_COMMAND="history -a; history -r;  $PROMPT_COMMAND"
shopt -s histappend
#bind '"\e[A": history-search-backward'
#bind '"\e[B": history-search-forward'

第一行是获取USERIP也就是用户会话的ip

第二行是设置history的时间格式,这里设置的格式为:history序号 2020-09-29 19:33:57  root@192.168.0.10: history

第三行设置的是history可以存放的历史命令最多可以存放10000行

第四行 history -a 追加本次会话新执行的命令到.bash_history中,也就是内存中的命令写入到历史文件中,history -r 读取历史文件中的所有历史命令到内存中的历史列表,即使内存中的列表中已经存在这条历史。

第五行 打开histappend选项,将其修改为on意为退出这个会话后,会向shell中发送一个sighup的信号,告诉它我退出了。

要记录登录者的用户名、IP、操作记录,在/etc/bashrc末尾加入几个环境变量,用于history命令显示用户ip等内容。完成后执行'source /etc/bashrc'即可。

#history
USERIP=`who -u am i 2>/dev/null| awk '{print $NF}'|sed -e 's/[()]//g'`
HISTSIZE=5000
HISTFILESIZE=5000
HISTTIMEFORMAT="%F %T ${USER_IP} `whoami` "
export HISTTIMEFORMAT

"history -a"用于将内存中的历史记录命令写入~/ .bash_history

"history -c"用于清除内存中的历史记录命令

"history -r"用于从~/ .bash_history读取历史命令到内存

history 实现机制

源码位置(/lib/readline/history.c)

1.Bash 的源码中(如 history.c 和 variables.c),以下函数处理 HISTFILE 相关逻辑:

history_initialize():初始化历史记录模块,读取 HISTFILE 或创建默认文件。

add_history():将新命令添加到内存历史列表。

write_history():将内存中的历史记录写入 HISTFILE(默认在 Shell 退出时调用)。

read_history():从 HISTFILE 加载历史记录到内存。

stifle_history():根据 HISTSIZE 和 HISTFILESIZE 截断历史记录。

bash/
├── history.c          # 历史命令相关实现
├── variables.c        # 变量管理相关实现
├── bashline.c         # 命令行编辑
├── builtins/          # 内置命令
│   ├── history.def    # history 命令实现
│   └── ...
├── lib/
│   └── history/       # 历史库(readline 相关)
│       ├── history.c
│       ├── histfile.c
│       └── ...
├── lib/readline/      # 行编辑库

2. HISTFILE 的工作流程
2.1 启动时:加载历史记录
读取 HISTFILE
Bash 启动时调用 read_history(),从 HISTFILE(默认 ~/.bash_history)逐行读取命令,填充到内存历史列表。
若文件不存在,则创建空文件(除非 HISTFILE 被显式设为 /dev/null)。
支持按行解析,忽略注释和空行。

应用 HISTSIZE 和 HISTFILESIZE
HISTSIZE:限制内存中保留的历史条数(超出部分从头部丢弃)。
HISTFILESIZE:限制 HISTFILE 的最大行数(超出部分从文件头部截断)。

2.2 运行时:管理历史记录
添加新命令
每次执行命令后,Bash 调用 add_history() 将命令追加到内存历史列表。
若启用 HISTCONTROL(如 ignoredups),会跳过重复命令。

实时保存(可选)
通过 PROMPT_COMMAND 设置(如 export PROMPT_COMMAND="history -a"),可在每次提示符显示前调用 history -a,将内存历史追加到 HISTFILE。避免 Shell 崩溃时丢失未保存的历史。

2.3 退出时:保存历史记录
调用 write_history()
Bash 退出时(或执行 exit/Ctrl+D),将内存历史写入 HISTFILE。默认行为是覆盖文件,但可通过 shopt -s histappend 改为追加模式。

截断历史文件
若 HISTFILESIZE 小于当前文件行数,Bash 会从文件头部删除旧条目,确保文件大小不超过限制。


修改历史文件路径和其它配置

export HISTFILE=~/.freeoa_custom_history

永久修改(写入 ~/.bashrc)
echo 'export HISTFILE=~/.my_custom_history' >> ~/.bashrc
source ~/.bashrc

#禁用历史记录(安全敏感场景)
# 方法1:指向 /dev/null
export HISTFILE=/dev/null

# 方法2:清空并设为只读
unset HISTFILE
readonly HISTFILE

# 方法3:在 ~/.bashrc 中条件禁用
if [ "$USER" = "root" ]; then
    export HISTFILE=/dev/null
fi

#多会话历史隔离(解决上文中所提及的多个终端会话情况下记录不全的问题)
# 每个终端使用独立历史文件
export HISTFILE=~/.bash_history_$(tty | sed 's/\//_/g')

# 或使用会话 ID
export HISTFILE=~/.bash_history_$$

#多会话历史共享(实时同步)
# 在 ~/.bashrc 中添加
export PROMPT_COMMAND="history -a; history -n; ${PROMPT_COMMAND}"

# 说明:
# history -a  将当前会话历史追加到文件
# history -n  从文件读取其他会话的新历史

常见用法:

# 备份历史文件
cp ~/.bash_history ~/.bash_history.backup.$(date +%Y%m%d)

# 合并多个历史文件
cat ~/.bash_history_* >> ~/.bash_history_merged

# 清空当前会话历史
history -c

# 清空历史文件
> ~/.bash_history

# 清空并退出
history -c && history -w && exit

~/.bashrc 配置
# ========== 历史命令配置 ==========
export HISTFILE=~/.bash_history
export HISTSIZE=100
export HISTFILESIZE=200

# 忽略重复和空格开头的命令
export HISTCONTROL=ignoreboth

# 忽略敏感命令
export HISTIGNORE="*password*:*secret*:ls:cd:exit"

# 多会话实时同步
export PROMPT_COMMAND="history -a; history -n"

# 显示时间戳(便于审计)
export HISTTIMEFORMAT="%F %T "

安全加固相关用法

#忽略敏感命令
# 在 ~/.bashrc 中添加
export HISTIGNORE="*password*:*secret*:*ssh*:sudo rm*:curl*wget*"

# 忽略以空格开头的命令(前面加空格不记录)
export HISTCONTROL=ignorespace

# 忽略重复命令
export HISTCONTROL=ignoredups

# 同时忽略空格开头和重复命令
export HISTCONTROL=ignoreboth

#临时禁用某条命令记录
# 命令前加空格(需设置 HISTCONTROL=ignorespace)
 ssh user@server

# 或使用 unset 临时禁用
unset HISTFILE

# 执行敏感命令
# 恢复
export HISTFILE=~/.bash_history

#审计场景(强制记录)
# 在 /etc/profile 中强制设置(对所有用户生效)
export HISTFILE=/var/log/bash_history_$USER
export HISTSIZE=10000
export HISTFILESIZE=50000
export PROMPT_COMMAND="history -a"

# 设置文件权限(防止用户修改)
chmod 444 /var/log/bash_history_$USER
chattr +a /var/log/bash_history_$USER  # 只能追加