Skip to main content

iTerm2 历史不丢失:scrollback 与 zsh history 双层配置

· 6 min read

iTerm2 历史丢失是两套独立机制,默认都是真丢弃

  1. scrollback 输出:默认 ~2000 行,clear/CLI 会发 CSI-3J 主动清空
  2. zsh history:HISTSIZE/SAVEHIST 默认 500/1000,只 exit 时写盘
  3. tmux 独立 history-limit,外层 iTerm scrollback 直接失效
  4. 已丢内容就是真丢,macOS 不留终端文本
  5. 长期任务用 script 录屏,iTerm 崩溃也能找回
  6. 关闭 Tab 自动存盘:Save scrollback to file when window is closed,默认目录 ~/Library/Application Support/iTerm2/Scrollback/

一、先分清两个东西

把 "iTerm2 历史" 当成一个东西,是踩坑的起点。它其实是 两个完全独立 的机制:

  • scrollback 输出日志npm run dev 打印出来的内容、报错堆栈、build 日志。在终端窗口里按滚轮往上翻看到的就是它。配置方:iTerm2
  • zsh history 命令列表history 命令查到的"你敲过的命令",存到 ~/.zsh_history配置方:zsh

两个机制跑在不同进程、由不同配置文件控制。下面分开讲。

二、scrollback 输出为什么会丢

iTerm2 的 scrollback 只放在 Tab 进程的内存里默认不写盘。两类动作会触发丢弃:

1. 缓冲区行数耗尽(默认 ~1000-2000 行)

新输出从底部进来,最顶部的旧行被释放内存。指针前移,旧内容彻底消失:

  • 内存里没有副本
  • macOS 页面缓存也不会留这些终端文本
  • 关闭 Tab 之后更找不回来

2. 程序主动发 \e[3J(CSI-3J)清空缓冲区

clearnpm run devclaude-code、各种 CLI 工具退出时会发送 \e[3J 指令,iTerm2 收到后 整块 scrollback 直接清空——不是隐藏,是真删。

判断到底是哪种情况:新开一个 Tab 跑一段很长的输出(比如 find / -name "*.log"),新 Tab 可以往上翻很长 → 旧 Tab 只是启动时读了旧配置;如果新 Tab 也翻不多 → 缓冲区或 CSI-3J 的问题。

修复

打开 iTerm2 设置 Cmd+,,进入 Profiles → Default → Terminal

  • Unlimited scrollback(无限回滚),或填一个具体上限如 200000
  • Save lines to scrollback in alternate screen mode(vim/less 退出后保留历史)

切换到 Advanced,右上角搜索框分别查找:

  • Prevent CSI 3 J from clearing scrollback historyYes
  • Save scroll buffer when clearing screenYes
  • Save scrollback to file when window is closedYes(关闭 Tab 时把整段回滚自动存盘,默认目录 ~/Library/Application Support/iTerm2/Scrollback/

Unlimited scrollback 会吃内存,长时间跑日志极大的进程(build、train、爬虫)时考虑改为 200000 留点余地。

三、zsh history 为什么不完整

history 命令查到的"你敲过的命令"由 zsh 自己维护,存到 HISTFILE(默认 ~/.zsh_history)。两个环境变量控制大小:

  • HISTSIZE:当前 shell 内存里缓存多少条
  • SAVEHIST:持久化到磁盘文件保留多少条

macOS 默认 HISTSIZE=500SAVEHIST=2000,且 默认只在 shell 正常退出(exit 或关闭窗口)时才把内存里的 history 写入磁盘。崩溃、断电、kill -9 进程 → 内存里没刷盘的命令直接丢。

修复

~/.zshrc 追加:

HISTFILE="$HOME/.zsh_history"
HISTSIZE=1000000
SAVEHIST=1000000

setopt INC_APPEND_HISTORY # 每条命令立即写盘,不等 exit
setopt EXTENDED_HISTORY # 附带时间戳
setopt SHARE_HISTORY # 多 Tab 共享历史
setopt HIST_IGNORE_ALL_DUPS # 去重

source ~/.zshrc 生效,校验 echo $HISTSIZE 输出 1000000 即成功。

INC_APPEND_HISTORY 是关键——只改 HISTSIZE/SAVEHIST 但不开启它,shell 崩溃时内存里的命令仍然没刷盘,照丢。

四、tmux 是另一层坑

在 iTerm2 里跑 tmux,外层 iTerm2 的 scrollback 直接失效——你滚轮翻的是 tmux 内部的缓冲区。tmux 默认 history-limit2000,上限到了旧内容直接释放。

修复

编辑 ~/.tmux.conf

set -g history-limit 100000
set -g mouse on
set -g terminal-overrides 'xterm*:smcup@:rmcup@'

tmux source-file ~/.tmux.conf 重载。

更彻底的方案:启动 tmux 用 tmux -CC(iTerm2 专属集成模式),把 scrollback 缓冲区交给 iTerm2 接管,规避 tmux 内部的 history-limit。代价是脱离了 tmux 原生会话管理逻辑(Ctrl-b 快捷键、detach/re-attach 由 iTerm2 接管)。

五、长期任务最稳妥方案

跑 build、爬虫、train、一次性脚本时,额外用 script 命令录屏,全程输出写到磁盘文件:

script -q session-$(date +%Y%m%d_%H%M%S).log
# 执行你的命令
exit

iTerm2 崩了、机器重启、内存撑爆,输出全在文件里。代价是退出后才能搜(grepless 文件),不能滚动。

如果用 tmux 且需要实时落盘:

tmux pipe-pane -o -t my-session 'cat >> ~/tmux-output.log'

完整配置清单(照抄即可)

iTerm2 图形界面(仅新 Tab 生效,旧 Tab 已丢的救不回来):

  1. Profiles → Terminal → ✅ Unlimited scrollback + ✅ Save lines to scrollback in alternate screen mode
  2. Profiles → Advanced → 三项全部设为 YesPrevent CSI 3 J from clearing scrollback history / Save scroll buffer when clearing screen / Save scrollback to file when window is closed

~/.zshrc

HISTFILE="$HOME/.zsh_history"
HISTSIZE=1000000
SAVEHIST=1000000
setopt INC_APPEND_HISTORY EXTENDED_HISTORY SHARE_HISTORY HIST_IGNORE_ALL_DUPS

~/.tmux.conf(tmux 用户):

set -g history-limit 100000
set -g mouse on
set -g terminal-overrides 'xterm*:smcup@:rmcup@'

长期任务script -q output.log,跑完别忘 exit

校验

echo $HISTSIZE # 1000000
echo $SAVEHIST # 1000000

两个都输出 1000000 即 zsh 配置生效。iTerm2 那边的设置没有命令行校验方式,开新 Tab 跑一段长输出验证:滚轮能翻回去说明 scrollback 生效;clear 之后旧内容还在说明 CSI-3J 屏蔽生效。

最后一句

这套配置 只能保护未来,已经丢的日志救不回来。所以第一次配置完成后,新开 Tab 跑个测试用例确认效果;之后跑重要长任务前,用 script 兜底——多一层磁盘记录总比事后翻历史靠谱。