iTerm2 历史不丢失:scrollback 与 zsh history 双层配置
iTerm2 历史丢失是两套独立机制,默认都是真丢弃。
- scrollback 输出:默认 ~2000 行,
clear/CLI 会发 CSI-3J 主动清空 - zsh history:HISTSIZE/SAVEHIST 默认 500/1000,只 exit 时写盘
- tmux 独立 history-limit,外层 iTerm scrollback 直接失效
- 已丢内容就是真丢,macOS 不留终端文本
- 长期任务用
script录屏,iTerm 崩溃也能找回 - 关闭 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)清空缓冲区
clear、npm run dev、claude-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 history→ YesSave scroll buffer when clearing screen→ YesSave scrollback to file when window is closed→ Yes(关闭 Tab 时把整段回滚自动存盘,默认目录~/Library/Application Support/iTerm2/Scrollback/)
Unlimited scrollback 会吃内存,长时间跑日志极大的进程(build、train、爬虫)时考虑改为
200000留点余地。
三、zsh history 为什么不完整
history 命令查到的"你敲过的命令"由 zsh 自己维护,存到 HISTFILE(默认 ~/.zsh_history)。两个环境变量控制大小:
HISTSIZE:当前 shell 内存里缓存多少条SAVEHIST:持久化到磁盘文件保留多少条
macOS 默认 HISTSIZE=500、SAVEHIST=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-limit 是 2000,上限到了旧内容直接释放。
修复
编辑 ~/.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 崩了、机器重启、内存撑爆,输出全在文件里。代价是退出后才能搜(grep、less 文件),不能滚动。
如果用 tmux 且需要实时落盘:
tmux pipe-pane -o -t my-session 'cat >> ~/tmux-output.log'
完整配置清单(照抄即可)
iTerm2 图形界面(仅新 Tab 生效,旧 Tab 已丢的救不回来):
Profiles → Terminal→ ✅ Unlimited scrollback + ✅ Save lines to scrollback in alternate screen modeProfiles → Advanced→ 三项全部设为 Yes:Prevent 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 兜底——多一层磁盘记录总比事后翻历史靠谱。