- CLI
- 开发工具
【免费下载链接】fish-shell
The user-friendly command line shell.
history是 fish-shell 中用于查看、检索、删除与维护交互式命令历史的核心命令,它由history函数(交互式封装)与historybuiltin(底层实现)双层协作构成。本文以 history.rst 为骨架,结合 history 函数、builtin 实现 与 历史存储模块 的源码,完整讲解 7 个子命令、全部选项、三种匹配模式、时间戳输出、批量删除交互,以及通过fish_history变量实现"会话级"历史隔离与私密模式等实战能力,读完即可熟练管理自己的命令历史。
命令全景:函数与 builtin 的双层结构
在 fish 中执行history时,实际先进入 share/functions/history.fish 中定义的history函数。该函数负责:
- 用
argparse解析选项与子命令(并校验--prefix、--contains、--exact三者互斥,--exact与搜索类选项互斥); - 提供交互式能力(删除前的确认提示、清空前的确认、通过分页器展示搜索结果);
- 将参数转发给真正的
builtin history完成实际读写。
而 src/builtins/history.rs 中的historybuiltin 才是直接操作 src/history/history.rs 中History对象的执行者,负责搜索、删除、合并、保存、清空、追加等磁盘操作。文档在描述各子命令时反复强调"这是history函数的特性"或"这是 builtin 的行为",正是因为交互行为与底层行为分属两层。
命令语法(Synopsis)
history [search] [--show-time] [--case-sensitive] [--exact | --prefix | --contains] [--max N] [--null] [--reverse] [SEARCH_STRING ...] history delete [--case-sensitive] [--exact | --prefix | --contains] SEARCH_STRING ... history merge history save history clear history clear-session history append COMMAND ...history用于搜索、删除以及以其他方式操作交互式命令历史(在文档中对应history-search一节)。所有选项既可以出现在子命令之前,也可以紧跟子命令之后。
七个子命令详解
search:搜索历史(默认操作)
history search返回匹配搜索字符串的历史条目;若不提供搜索字符串,则返回全部历史条目。当未指定任何子命令时,search就是默认操作——只有当你恰好要搜索search、delete、merge等子命令同名关键词时,才需要显式写出history search。
默认使用--contains(包含匹配)模式,除非你显式指定了其他匹配方式。条目默认按从新到旧排序,使用--reverse可反转。当 stdout 连接到终端(tty)时,history函数会把输出通过分页器展示(默认调用__fish_anypager,即less等工具,并在$LESS未预设时为其设置--quit-if-one-screen --RAW-CONTROL-CHARS等参数,见 share/functions/history.fish 第 76-100 行);而 builtin 本身只把结果写入 stdout。
从 src/history/history.rs 的search方法可以看到底层行为:搜索按--max限制匹配数量,逐条调用format_history_record格式化输出;非反向时边搜边输出,反向(--reverse)时先收集再整体输出;当输出流写入失败(例如用户按 Ctrl-C 中断分页)时会中止搜索。
delete:删除历史条目
history delete删除匹配的历史条目,默认使用--exact(精确匹配)模式。若未指定--exact,删除前会弹出一个交互提示,让你选择要删除哪些条目:
- 输入单词
all:删除全部匹配条目; - 输入方括号中的单个 ID(如
[3]):仅删除该条目; - 输入多个 ID,或空格分隔的 ID 区间:批量删除(如
7 10..15 35); - 直接回车:不删除任何内容。
这一交互删除行为由history函数实现(share/functions/history.fish 第 102-186 行):函数先以--null模式搜索出匹配条目并编号打印,再通过read读取用户选择,支持5..12这类区间展开、path sort -u去重排序后逐条调用builtin history delete --exact --case-sensitive删除。builtin 层只支持--exact --case-sensitive的删除方式,见 src/builtins/history.rs 第 296-314 行:若指定了非--exact模式会报错builtin history delete only supports --exact,若未加--case-sensitive会报错builtin history delete --exact requires --case-sensitive。
merge:立即合并其他会话的历史
通常情况下,fish 会忽略在当前会话之后启动的会话所做的历史变更。history merge会立即把这些外部变更吸收进当前会话(底层对应history.incorporate_external_changes())。在私密模式下(fish_history被置为空字符串)无法执行 merge,builtin 会报错can't merge history in private mode。
save:立即写入历史文件
history save立即把所有变更写入历史文件。shell 本身会自动保存历史,该命令是为内部使用提供的,普通用户一般无需手动调用。
clear:清空历史文件
history clear清空整个历史文件。除非使用builtin history(跳过函数封装),否则删除前会显示确认提示,要求输入yes才真正清空(见 share/functions/history.fish 第 192-211 行)。builtin 层对应history.clear()后立即history.save()。
clear-session:清空当前会话的历史
history clear-session只清空当前会话产生的历史记录。注意:如果在某个会话中执行过history merge或builtin history merge,那么 merge 之前被并入的内容不会被清除,只有 merge 之后产生的历史会被擦除。builtin 层对应history.clear_session()后history.save()。
append:无需执行即可追加历史
history append COMMAND ...把给定命令追加进历史,而不需要真正执行它。这常用于脚本中批量注入历史,或恢复误删的条目。函数在未提供参数时会交互式提示Command:(见 share/functions/history.fish 第 215-222 行),builtin 层则对每个参数调用history.add_commandline()。
选项参考
| 选项 | 含义 | 适用范围 |
|---|---|---|
-C/--case-sensitive | 大小写敏感匹配。默认是大小写不敏感 | search / delete |
-c/--contains | 匹配包含指定文本的条目。search的默认模式。delete 暂不支持 | search |
-e/--exact | 精确匹配指定文本。delete的默认模式。注意默认仍然大小写不敏感,真正区分大小写须加-C | search / delete |
-p/--prefix | 匹配以指定文本开头的条目。delete 暂不支持 | search |
-t/--show-time[=格式] | 为每条历史条目前置记录时间。默认 strftime 格式为# %c%n,可自定义如--show-time="%Y-%m-%d %H:%M:%S "或--show-time="%a%I%p"。短选项-t不接受格式串、只使用默认格式。支持任意 strftime 格式,包括%s输出自 epoch 起的原始 Unix 秒 | search |
-z/--null | 搜索输出以 NUL 字符而非换行符结尾。便于用read -z处理多行历史条目 | search |
-n N/--max N | 只输出前 N 条匹配条目。仅对history search有效 | search |
-R/--reverse | 结果按从旧到新排序(大多数 shell 的顺序),默认是从新到旧 | search |
--color WHEN | 控制历史条目语法高亮着色时机。WHEN为auto(默认,仅当输出是终端时着色)、always、never | search |
-h/--help | 显示命令帮助 | 全局 |
源码佐证:builtin 的选项解析定义在 src/builtins/history.rs 第 72-89 行(SHORT_OPTIONS = "CRcehmn:pt::z"与LONG_OPTIONS表)。其中-p/-c/-e分别映射到底层SearchType::PrefixGlob、ContainsGlob、Exact(src/history/history.rs 第 59-75 行定义了完整的SearchType枚举),-t是可带可选参数的选项,缺省格式串时回落到# %c%n;-n/--max通过fish_wcstol解析为数字,非数字时报NOT_NUMBER错误。
函数层的argparse还额外注册了t/show-time=?(问号表示可选参数)与n#max、color=等,并把--prefix、--contains、--exact声明为互斥(--exclusive 'c,e,p',见 share/functions/history.fish 第 7-12 行)。
实战示例
history clear # 删除全部历史条目(函数层会先要求输入 yes 确认) history search --contains "foo" # 输出所有包含字符串 "foo" 的历史命令 history delete --prefix "foo" # 交互式删除所有以 "foo" 开头的命令。 # 你可以用空格分隔的多个 ID,或类似 "5..12" 的区间一次选中多条。更完整的组合示例:
# 只看最近 20 条历史,并带时间戳 history --show-time="%Y-%m-%d %H:%M:%S " --max 20 # 大小写敏感、从旧到新查找包含 "SSH" 的条目 history search --contains --case-sensitive --reverse SSH # 把搜索结果以 NUL 结尾输出,方便管道处理 history search --null "git" | read -z # 非交互式精确删除(builtin 层,必须同时 --exact 与 --case-sensitive) builtin history delete --exact --case-sensitive "rm -rf /tmp/x" # 手动把另一终端会话的历史并入当前会话 history merge自定义历史文件名:fish_history变量
默认情况下,交互式命令会被记录到$XDG_DATA_HOME/fish/fish_history(通常是~/.local/share/fish/fish_history)。
你可以为当前 shell 会话把fish_history变量设置为其他名字:
- 变量未设置时,默认值是
fish,对应$XDG_DATA_HOME/fish/fish_history; - 若设为例如
fun,历史将被写入$XDG_DATA_HOME/fish/fun_history; - 设置为空字符串意味着完全不存储历史——这与浏览器中的"无痕/私密会话"特性类似。
你可以随时修改fish_history(例如set -x fish_history "session_name"),立即生效。若将其设为"default",则使用默认会话名(即"fish")。
源码层面,src/history/history.rs 的history_id_from_var(第 1690-1714 行)精确实现了上述规则:
- 变量不存在 → 使用默认会话名
fish(DFLT_FISH_HISTORY_SESSION_ID); - 变量为空字符串 → 返回
Memory(MemoryHistoryId::PrivateMode),即纯内存私密模式,不落盘; - 变量是合法变量名 → 返回
Disk { session_id },对应独立的磁盘历史文件; - 变量名非法(不是合法变量名)→ 记录错误日志并回退到默认
fish。
而"立即生效"则由 src/env_dispatch.rs 第 196-199 行的handle_fish_history_change实现:fish_history变更时调用reader_change_history(history_id),让读取器立即切换到新会话历史。这也解释了为何history merge在私密模式下被拒绝(in_private_mode检查,见 src/builtins/history.rs 第 334-339 行)。
与 bash/zsh 的HISTFILE有何不同
bash 与 zsh 使用名为HISTFILE的变量实现类似功能。fish 使用不同名字是为了避免冲突并表明行为不同:fish 接受的是"会话名"而非"文件路径"。此外,只要把fish_history设为fish或default以外的值,就会阻止导入 bash 历史——这个功能最常见的用途是在做演示时避免泄露私人或敏感的历史记录。
底层存储原理:append-only、压缩与 mmap
从 src/history/history.rs 顶部的模块注释可以看到 fish 多会话并发写历史的完整策略,这也是理解merge/save/clear-session语义的基础:
- 所有历史文件都是 append-only 的——数据一旦写入就不再修改;
- 历史文件可以被重写("vacuum"):读入整个文件并写出新文件,同时执行维护任务——按 LRU 方式丢弃条目直到达到期望的最大数量、去除重复条目、按时间戳排序(计划中,尚未实现)。新文件通过
rename()原子替换旧文件; - 历史文件通过
mmap()映射,每个条目只需在内存中保存一个usize(偏移量),按需惰性加载,从而降低内存消耗; - 对历史文件的访问需要同步,默认通过
flock()加锁(实现在src/fs.rs);若不可用,则使用一种不完美的回退方案——检测竞争并在检测到竞争时重试。
此外模块中还定义了PersistenceMode(Disk/Memory/Ephemeral)用于控制单条历史项的落盘方式,以及VACUUM_FREQUENCY = 25的压缩频率常量。历史文件的序列化与解析位于 src/history/yaml_backend.rs(YAML 风格格式),src/history/file.rs 负责 mmap 映射、条目解码与文件类型推断。
注意事项与兼容性
--prefix与--contains同时指定时,以最后一个出现的标志为准(函数层用argparse --exclusive声明互斥,builtin 层逐个覆盖search_type)。--contains与--prefix目前不被delete子命令支持(builtin 只支持--exact --case-sensitive删除;函数层会对非精确模式先交互列出候选再逐个精确删除)。- 历史子命令也可以写成对应的长选项形式,这是为了向后兼容而保留的:例如
history search可写作history --search,同理--delete、--merge、--save、--clear分别对应各子命令。这些长选项已被弃用,将在未来版本中移除。函数层在 share/functions/history.fish 第 10-12 行也标注了S-search D-delete M-merge V-save X-clear为 deprecated 选项,builtin 侧的LONG_OPTIONS中同样以私有字符码保留了这些映射(src/builtins/history.rs 第 82-86 行),源码注释明确"为保持 fish 3.0 之前的行为不被破坏"而暂缓移除。 --max只对search有效;clear、clear-session、merge、save不接受任何选项与参数(builtin 的check_for_unexpected_hist_args会校验并报subcommand takes no options/ 参数个数错误)。- 交互式补全:执行
history时可通过 Tab 补全子命令与选项,具体规则定义在 share/completions/history.fish——--prefix/--contains/--exact/--show-time/--case-sensitive对search与delete有效,--max/--null/--reverse/--color仅对search有效,且刻意不为内部使用的save提供补全。
- CLI
- 开发工具
【免费下载链接】fish-shell
The user-friendly command line shell.
相关推荐
Arthas history 命令全解析:查看、检索与清理 Java 诊断会话的命令历史
Arthas history 命令全解析:查看、检索与清理 Java 诊断会话的命令历史 本文以 Arthas 官方文档 history 命令说明 https:
开发工具可观测性调试器性能剖析radare2历史功能:命令历史与会话管理终极指南
radare2历史功能:命令历史与会话管理终极指南 radare2作为一款强大的UNIX like逆向工程框架,提供了完整的命令历史与会话管理功能,让逆向工程工
逆向工程网络安全Powerlevel9k命令历史统计:history segment实用配置
Powerlevel9k命令历史统计:history segment实用配置 你是否经常在终端中工作时忘记自己执行过哪些命令?是否想快速了解当前会话的命令执行情
开发工具CLI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考