尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

终端、Shell、提示符与复用器:六层链路排错指南

终端、Shell、提示符与复用器:六层链路排错指南 命令行这东西用得越久越容易发现自己其实没搞清它。我见过不少人用了五六年 Linux仍然把那个黑框叫作 Shell把提示符当成终端自带的一部分也见过有人在服务器上折腾了半小时最后发现问题只是提示符里一个转义符没加括号。这篇就把终端、终端模拟器、Shell、提示符、插件、复用器这六个概念从头到尾捋一遍重点讲它们各自的职责边界、彼此之间怎么衔接以及出错时该从哪一层开始查。不管你是刚学 shell 脚本入门的新手还是天天跟 Linux shell 命令打交道的老手把这条链路拆清楚之后很多原本莫名其妙的报错会变得非常好定位。1. 从按下回车到屏幕上出现一行字这条链路上有六个角色1.1 一次ls -l的完整旅行先别急着背概念我们跟着一次真实的输入走一遍。你在键盘上敲下l、s两个字母然后按回车屏幕上出现一列文件名。整个过程大概是这样终端模拟器先捕获你的按键把它转换成字节流写进一个叫伪终端主设备PTY master的东西内核里的 TTY 子系统接住这串字节转发给伪终端从设备PTY slaveShell 正阻塞在从设备上做读操作读到ls -l\n之后开始解析——它先判断这是内建命令还是外部程序发现ls不在内建列表里于是去 PATH 里找找到/usr/bin/ls然后 fork 一个子进程、exec 过去ls把结果写到标准输出也就是那个从设备数据再沿着 TTY 子系统回流到主设备终端模拟器读到输出按字符编码和当前配色渲染到屏幕上最后 Shell 打印一个新的提示符回到等待状态。这条链路上终端模拟器负责搬运和渲染Shell 负责解释和执行内核负责中转和信号。三者各管一段职责非常清楚。之所以很多人搞混是因为绝大多数时候三者是同时出现在一个窗口里的视觉上完全融成一体。可一旦要排错能不能分清是哪一层出的问题直接决定你是五分钟解决还是半小时打转。举个特别常见的例子你在终端里跑一个需要交互输入的程序把它接进管道之后行为就变了。比如python直接运行时可以按上下键翻历史echo print(1) | python就不行了。这不是 bug是程序通过isatty()检测到自己的标准输入不再是一个终端设备于是主动关掉了行编辑功能。理解这一点你就能明白为什么有些命令在管道里和在命令行里表现不一样也不会再把锅甩给 Shell。1.2 分清这六层能省下什么我做运维那几年最深的体会是排错的速度取决于你能不能把症状映射到具体的层。下面这张表是我自己总结的对照关系遇到问题可以直接对号入座。症状表现最可能的层第一手排查动作字符显示成方块、颜色不对终端模拟器换字体、检查 TERM 与真彩色声明上下键翻不出历史、补全失效Shell检查 readline 配置、确认是哪个 shell提示符换行错乱、长命令覆盖上一行提示符检查非打印字符是否用\[ \]包裹关掉窗口后进程被杀复用器缺失用会话复用工具挂住进程脚本在 A 机器能跑 B 机器不行解释器 / 编码file、cat -A看换行符和 BOM快捷键按了没反应层间冲突确认快捷键归终端还是归 Shell这张表里最容易被忽略的是最后一行。同样是CtrlA在终端里可能是全选在 readline 里是光标移到行首在某个复用器里又可能是别的功能。按键事件是由内到外逐层消费的谁先接住谁处理后面的人根本看不到。搞不清这个顺序改配置就纯靠碰运气。还有一点值得强调这六层是可以独立替换的。你可以把默认终端换成另一个Shell 从 bash 换到 zsh复用器从一种换到另一种彼此之间靠标准接口通信。正因为可替换所以才值得单独理解每一层——你不是在学某一个具体软件而是在学一套通用的分层模型。2. 终端模拟器那个黑框一直在假装自己是一台老式设备2.1 TTY、PTY 与假装背后的历史包袱终端这个词最早指的是物理设备——那种带键盘和打印机的电传打字机。后来硬件被软件替代了但内核里那套抽象被完整保留下来这就是 TTY 子系统。今天你打开的每一个终端窗口内核都会为它分配一对伪终端设备一个主一个从中间靠内核转发。所谓终端模拟器本质就是一个读写伪终端主设备的图形程序。为什么这个假装这么重要因为几乎所有 Unix 工具在设计时都假设标准输入可能是一个终端。它们会通过isatty()来问内核我这头连着的是不是真人如果是就启用行缓冲、回显、彩色输出、进度条如果不是就切成批处理模式去掉一切装饰。这个判断逻辑深植于各类工具里改不了也没必要改。理解了这一点很多玄学就有了答案。比如为什么ls在命令行里有颜色、重定向到文件后颜色没了为什么git在管道里不显示进度为什么有些程序检测到非终端环境就拒绝交互式提问直接报错退出。这些都不是程序在针对你而是它们在老老实实按终端检测的结果走。你要是真需要强制彩色输出得显式加参数比如让相关工具忽略这个检测而不是去改终端设置。2.2 渲染、按键映射、复制粘贴这些琐事到底归谁职责划分上终端模拟器管的是看得见的部分字体、行高、字符宽度、配色方案、真彩色支持、滚动缓冲、鼠标选区、窗口标题、快捷键。Shell 管的是行为部分命令历史、Tab 补全、行内编辑、别名、函数。这里有个特别典型的误解很多人以为按上键能翻出上一条命令是终端的功能。不是。那是 Shell 通过 readline 或者 zlezsh 的行编辑器实现的。你在终端设置里翻遍所有选项也找不到这个开关因为它压根不在那一层。另一个高频误区是字符宽度。中文、日文、韩文属于宽字符占两列而 emoji 的宽度在不同终端里判断标准还不一样。这个宽度计算如果不是终端在做提示符和表格就会错位。我以前写过一个输出对齐的脚本在本地终端上整整齐齐一换到别的终端就全乱最后发现是宽字符列宽判断差异导致的。解决办法很简单处理含中文的列对齐时用工具库显式计算显示宽度而不是一厢情愿地按字符个数算。复制粘贴也归终端管但它和复用器之间存在竞争关系。你开了复用器的鼠标模式之后拖拽选中可能被复用器截走这时想用终端原生的选区功能通常按住 Shift 拖拽就能绕过。这个技巧几乎没人写在文档显眼处但特别实用。2.3 挑终端模拟器时我真正会看的几个指标市面上的终端模拟器很多功能列表一个比一个长。以下是我实际会关注的几个维度按重要性排序真彩色与宽度处理能不能正确显示 24 位色CJK 和 emoji 的宽度判断准不准。这直接影响日常观感比什么花哨特效重要得多。渲染方式GPU 加速在滚大日志时确实流畅但也更吃显卡驱动远程桌面环境下不一定划算。分屏与标签页如果你不打算用复用器这一项权重就高如果本来就常驻复用器终端自带分屏反而多余。与复用器的兼容性某些终端对复用器的颜色透传、鼠标模式、剪贴板集成处理得不好会导致体验割裂。启动速度听起来无所谓但如果你一天要开几十个窗口累计起来很可观。我的建议是先确定你用不用复用器再决定终端怎么选。这两者的功能有大量重叠同时上满功能往往只是互相打架。3. Shell真正读懂并执行你命令的那一层3.1 交互式 Shell 和脚本 Shell 是两个物种这是最容易踩坑的地方。同一个 Shell交互式运行和跑脚本时的行为差异非常大交互式会加载个人配置文件、开启行编辑和补全、支持别名和历史记录非交互式只读很少的几个环境变量别名默认不展开历史也不写。所以我在终端里明明能跑写进脚本就报 command not found这种问题八成是因为你依赖了别名或者某个只在交互式配置里定义的东西。启动文件的加载顺序也值得记一下它决定了你写的配置到底在什么时候生效场景bash 读取顺序zsh 读取顺序登录交互/etc/profile → ~/.bash_profile → ~/.bash_login → ~/.profile/etc/zshenv → ~/.zshenv → /etc/zprofile → ~/.zprofile → /etc/zshrc → ~/.zshrc → /etc/zlogin → ~/.zlogin非登录交互~/.bashrc~/.zshenv → ~/.zshrc非交互脚本由 BASH_ENV 指定~/.zshenv注意 bash 的一个经典陷阱登录 Shell 只读.bash_profile而.bashrc不会自动被读。很多人把 PATH 和别名写在.bashrc里结果 SSH 一登录发现全没生效就是因为这个。标准做法是在.bash_profile里显式 source 一次.bashrc。3.2$()、${}、$[]长得像干的事完全不同这三个符号在 shell 脚本基础知识里属于必考项但真讲清楚的人不多。它们的区别是$(命令)是命令替换把命令的输出当成字符串塞进当前位置。老写法是反引号但反引号不能嵌套$( )可以。${变量}是参数展开用来界定变量名边界还支持一堆修饰${var:-默认值}、${#var}取长度、${var/a/b}做替换、${var%.txt}去后缀。$((表达式))是算术展开做整数运算。$[ ]是它的过时写法别再用。一个高频误解是把${}当成某种花括号版变量访问其实它最大的价值在于消除歧义。比如echo $file_suffix和echo ${file}_suffix前者会把file_suffix整个当成变量名后者才是你想要的结果。还有一个性能层面的坑$( )会 fork 出一个子进程。在循环里写$(cat 小文件)这种几万次迭代下来开销相当可观。能改成变量就改成变量能改成 Shell 内建就改内建。3.3 for 循环、shift、批量重命名这些基础件的正确姿势for循环至少有三种形态用途完全不同# 遍历一个列表 for name in alpha beta gamma; do echo $name done # 类 C 风格适合计数 for ((i 0; i 10; i)); do echo 第 $i 次 done # 遍历文件注意用通配符而不是命令替换 for f in *.txt; do echo $f done第三条里那句用通配符而不是命令替换很关键。for f in $(ls)是经典反模式一旦文件名里有空格就崩成好几个字段而且ls输出本身也不是为机器解析设计的。要么用 glob要么用find ... -print0配合while IFS read -r -d 逐个读。批量重命名就用参数展开最干净# 先把动作打印出来确认确认无误再去掉 echo for f in *.txt; do echo mv -- $f ${f%.txt}.md done先 echo 再执行这个习惯我强烈建议养成。批量操作一旦写错文件名是不可逆的重命名和删除尤其危险。shift用在位置参数处理上每调用一次就把$2变成$1、$3变成$2左边的参数被丢弃。配合while [ $# -gt 0 ]可以逐个消费参数写带选项的脚本时非常常用。有个坑是如果你在函数里对$做了操作又 shift在旧版本 Shell 里有行为差异稳妥起见先local args($)拷一份再处理。3.4 命令查找顺序别名、函数、内建、PATH当你敲下一个命令时Shell 是按固定顺序找的别名 → 函数 → 内建命令 → PATH 里的可执行文件。这个顺序决定了覆盖规则也决定了很多为什么我改了却没用的问题。想看清某个命令到底解析成了什么用type -a cmd它会把所有匹配项都列出来。command -v比which更可靠因为which本身就是个外部程序它不知道别名和函数的存在。PATH 的顺序同样重要。如果你自己编译的东西装在/usr/local/bin而系统自带版本在/usr/bin那么谁在 PATH 前面谁生效。改完 PATH 后如果发现还是旧的多半是 Shell 的哈希缓存没更新——hash -r清一下就好了。这个缓存机制是为了避免每次都去遍历磁盘找命令但代价就是你换了路径它不知道。另外提醒一句在脚本里不要依赖别名。别名是给交互式使用准备的人性化糖脚本里应该用函数或者直接把完整命令写出来可读性和可移植性都更好。4. 提示符一行字符串其实是 Shell 的状态面板4.1 PS1 里的转义符到底怎么读提示符看着简单实际是一个在每次命令执行完都要重新求值的模板。bash 用PS1常见转义符有转义符含义转义符含义\u当前用户名\h主机名到第一个点\w当前目录全路径\W当前目录名\t24 小时制时间\d日期\$普通用户显示$root 显示#\!历史命令编号\#当前会话内的命令序号\eESC 转义符用于颜色颜色写法是\[\e[32m\]这种形式必须用\[和\]把非打印字符包起来。这两个符号是告诉 readline这段东西不会在屏幕上占宽度算列宽的时候跳过。要是漏了Shell 就会以为提示符比实际更长导致你在长命令里换行时整行错位光标跑到奇怪的位置。这个坑几乎每个人都会踩一次而且症状诡异到让人怀疑终端坏了。如果你用的是 zsh机制类似但要加%{ %}包裹非打印序列同时用%F{green}这类写法更省事。4.2 把 Git 分支、退出码和命令耗时塞进去提示符真正有用的地方在于承载状态信息。最常见的三类Git 分支和状态。bash 里可以调__git_ps1zsh 里通常通过插件或者 vcs_info 拿到。要注意的是如果你直接在 PS1 里写$(git branch ...)每次渲染都会真的去跑一遍 git在大仓库上能明显感到卡顿。上一条命令的退出码。这个必须小心$?是易失的你在提示符里做的任何操作都可能把它覆盖掉。正确做法是在提示符展开的最开始就把它存进变量PROMPT_COMMAND__last_status$? PS1\[\e[31m\]${__last_status}\[\e[0m\] \u\h:\w\$ 或者在 zsh 里用precmd钩子时机更可控。命令耗时。zsh 可以在preexec里记下开始时间在precmd里算出差值超过某个阈值才显示出来。这套组合我用得最多尤其是排查刚才那条命令怎么这么慢的时候一眼就能看出来。# zsh 示例只在耗时超过 2 秒时显示 preexec() { __cmd_start$EPOCHSECONDS } precmd() { local elapsed$(( EPOCHSECONDS - __cmd_start )) (( elapsed 2 )) echo 上条命令耗时 ${elapsed}s }4.3 提示符卡顿的排查思路提示符变慢的根源几乎只有一个渲染频率太高而每次渲染都做了重活。它是在你每按一次回车之后都要跑一遍的代码一旦里面有耗时操作感知会极度明显。按嫌疑度排序第一是每次渲染都调用外部命令比如 git、kubectl、各种云 CLI 的上下文查询第二是访问网络路径或者慢速挂载盘上的文件第三是在提示符里写了大段 Shell 逻辑。定位方法很简单在当前 Shell 里time一下你的提示符渲染函数或者把提示符临时改成一个静态字符串感受差别。优化策略我一般这么选能缓存就缓存比如 Git 分支状态按目录缓存带过期时间能异步就异步后台算好再下一次渲染时用能合并就合并一次调用拿全部信息而不是分四五次。实在不行就砍功能提示符的信息密度和响应速度之间必须做取舍我个人更偏向响应速度。5. 插件给这套链条加装外挂的正确姿势5.1 编辑器插件和 Shell 插件根本不是一回事这两类东西经常被混在一个话题里聊但它们的运行机制和风险等级差得很远。编辑器插件运行在编辑器自己的扩展宿主进程里通过官方 API 访问编辑器的功能出了问题通常只影响编辑器本身最坏情况是把它拖慢或者搞崩。Shell 插件就完全是另一回事了它本质上是被 source 进你当前 Shell 进程的脚本运行在同一个进程里可以随意修改你的环境变量、覆盖你的函数、重定义你的快捷键。它能给你带来巨大便利也能在你毫无察觉的情况下改掉cd的行为或者让每个新开的窗口都慢上两秒。所以对这两类的态度应该不同编辑器插件可以放手装出问题最多卸载Shell 插件要精挑细选而且必须知道它改了你的什么。装之前先读一遍它的源码特别是那些会 hook 到提示符渲染、命令执行前后钩子的部分。5.2 插件管理器的取舍手动 source 的优点是透明你能完全掌握加载顺序和加载内容缺点是管理和更新麻烦。用包管理器或者社区流行的插件框架优点是更新方便、依赖自动处理缺点是引入了一层抽象出问题时你搞不清到底是谁干的。我的取舍标准大概是这样几条看它的维护活跃度看它是否引入额外的二进制程序看它是否 hook 到了每次命令执行或每次提示符渲染这种高频路径看它的加载是同步还是异步。第四条尤为重要——一个 hook 在每次提示符渲染上的插件哪怕单次只多花 30 毫秒在你一天敲几百条命令的情况下也会变成明显的钝感。还有一条经验插件数量不是问题插件类型才是。装二十个只提供命令别名的插件没什么代价装两个每次都跑脚本的才要命。5.3 插件拖慢启动的定位方法新开一个 Shell 感觉卡先量化。zsh 有内置的性能剖析# 看整体启动耗时 time zsh -i -c exit # 用 zprof 看各段代码的耗时排名 # 在 .zshrc 最前面加 zmodload zsh/zprof最后面加 zprofbash 没有这么现成的工具可以用粗暴但有效的二分法把配置文件的后半部分注释掉再看启动时间缩小到某一段之后再往里二分。虽然土但三分钟就能定位。我踩过的一个具体坑是某个补全插件在初始化时去扫描了所有 PATH 目录下的可执行文件PATH 里挂了一个网络盘于是每次开终端都要等好几秒。这种问题用time一测就露馅但如果你只是觉得最近好像变慢了可能几个月都发现不了。养成定期量一下启动时间的习惯比什么优化技巧都有用。6. 复用器让会话脱离窗口活下来6.1 为什么要引入会话这层抽象先说清楚它解决什么问题。你通过 SSH 连到远程机器跑了一个要几小时的编译任务然后网络抖了一下连接断了。这时发生了什么连接断开终端设备消失内核给前台进程组发挂断信号大多数程序收到这个信号就退出了。你的编译任务就这么没了。复用器的做法是在远程机器上启动一个常驻的守护进程然后让你的 Shell 挂在这个守护进程下面。你的终端窗口只是附着到这个会话上关掉窗口相当于摘掉一个显示器后台的进程什么都不知道继续跑。下次连上来重新附着画面原样恢复。这个机制带来的好处远超防断线你可以一个会话开多个窗口和面板在本地不同终端之间切换同一组会话可以把输出落盘留档可以让多个终端同时附着同一会话做演示还可以在不打断前台任务的情况下临时开个面板查点东西。6.2 分屏、断线重连、日志留存的实际用法几个日常高频操作值得展开说一下。命名会话。用默认的自动编号时间长了谁也记不住哪个是哪个。建议按用途命名比如构建用一个、日志查看看一个附着的时候直接指定名字不用先列表再挑。日志留存。有个很好用但容易被忽略的功能是把面板输出持续写入文件。做法是给面板挂一个管道把流过来的内容用追加方式写进文件。这对事后复盘特别有价值——比如一个只在凌晨出现的问题你早上起来直接翻文件就行不用守着屏幕。滚动缓冲。默认保留的行数往往不够跑一次全量构建就冲掉了。这个值可以直接在配置里调大到几十万行。代价是内存占用但现代机器上这点开销基本可以忽略。同步输入。多个面板同时执行同一条命令做集群批量操作时能省很多事。但这也是把双刃剑我强烈建议用之前先确认每个面板当前的工作目录和状态不然在 A 机器上执行了只该在 B 机器上跑的命令后果自己承担。6.3 嵌套复用器、剪贴板与颜色这几处细节双层嵌套的前缀键冲突是最常见的问题。你先在本地开一层再在里面连远程又开一层两层的默认前缀键一样按下去只会被外层吃掉。解决办法有两个给其中一层换个前缀键或者直接用连续按两次的方式来透传给内层。我一般选前者改一次配置一劳永逸。剪贴板的问题前面提过核心是选区归属。终端原生选区和复用器选区会抢鼠标事件按住 Shift 拖拽通常可以绕过复用器直接走终端选区这个技巧值得记住。颜色降级也常遇到。有些场景下外层软件会声称自己不支持真彩色导致内层程序按 256 色输出颜色虽然不至于出错但看起来很灰。检查方法是在最内层打印一段颜色测试逐层排查到底是谁在降级。解决办法通常是在配置里显式声明支持真彩色。还有一个细节调整窗口大小之后的内层程序重绘。窗口尺寸变化会产生一个窗口变更事件逐层传递到内层程序程序收到后重新计算换行。如果中间某一层没处理好就会出现画面错乱、文字重叠。这种情况重新附着一次通常能恢复长期方案是保持各层软件版本较新。7. 几个真实故障的排查链路7.1[no write since last change]和/bin/sh: wq: command not found这两个报错看起来很唬人本质上是同一个原因你把编辑器里的命令敲到了 Shell 里。第一种是退出编辑器时它提示你有未保存的修改还不允许强制退出第二种是你在 Shell 提示符下直接输入了wqShell 找不到这个命令。为什么会搞混因为编辑器占据整个屏幕退出后 Shell 提示符出现的位置和编辑器命令行长得很像尤其是在快速连续操作的时候很容易没意识到自己已经退出来了。我的习惯是给编辑器明确的状态指示比如在界面上始终显示当前模式或者用不同颜色的状态栏区分。另外从编辑器退出后先看一眼提示符再敲下一条命令这个半秒钟的动作能避免大量误操作。排查这类问题的通用思路是先确认我现在在哪个程序里。看提示符的样式、看光标形状、看底部状态栏、按一下 ESC 看有没有反应都是快速判断手段。想强制退出不保存编辑器里通常有对应的命令具体记一下就好。这个坑的价值不在于它本身而在于它提醒你同一个键盘输入在不同程序里有完全不同的含义时刻知道自己在哪一层。7.2 脚本换台机器就跑不起来换行符、BOM 和解释器这是我最常遇到的跨平台问题症状往往诡异到离谱脚本内容和本地一模一样报错却是某个明明存在的命令找不到而且报错信息里那个命令名你根本没见过。第一个嫌疑是换行符。Windows 风格的\r\n在 Unix 下会被当成内容的一部分\r回车符会附着在行尾。于是#!/bin/bash\r变成了解释器路径的一部分系统找不到退而用默认的 sh 去跑或者某个变量后面带上了一个不可见的\r在比较时永远不相等。判断方法很简单file script.sh # 会明确告诉你 CRLF line terminators cat -A script.sh | head # 行尾显示为 ^M$ 就是 \r\n od -c script.sh | head -1 # 看开头有没有 \357\273\277BOM解决办法用dos2unix转换或者用支持指定换行符的编辑器重存一遍。BOM 的处理类似编辑器里选无 BOM 的 UTF-8保存即可。第二个嫌疑是解释器路径。写死绝对路径的 shebang 在换环境后可能失效尤其是介于两种主流 Shell 之间存在路径差异的时候。更稳妥的做法是用环境查找的形式让系统自己去找解释器。但要注意这种方式在某些精简容器镜像里也可能找不到得先确认目标环境里确实有。第三个嫌疑是依赖的命令不存在。脚本里用到某个工具本地装了目标机器没装或者版本不同参数不一样。这类问题没有捷径就是在脚本开头做一次依赖检查缺什么直接报清楚别等跑到一半才失败。7.3 移动端和远程调试时的几个注意点用adb shell进到设备上操作很多人会下意识以为是标准的 Linux 环境。其实不是。设备上的 sh 往往是精简实现bash 的大量特性不存在数组、[[ ]]、进程替换、local的某些行为都可能不支持。写要在手机上跑的脚本最好先确认目标环境到底是什么 Shell用最基本、最可移植的语法。另外通过调试桥执行命令时要意识到你是在改设备的真实状态。比如把电池充电状态从 USB 切成其他模式来测试应用的电量逻辑测完记得恢复成原来的值否则下次真机测试时数据全是歪的还得回头找原因。这类改了状态忘了复位的问题在移动端调试里非常普遍。如果要自动化交互式的命令行程序expect 是个经典选择。它的核心思路是等出现某段输出就发送某段输入配合超时控制。写这类脚本时一定要给每段等待设置合理超时并且处理超时分支。我见过太多脚本因为超时设成了无限等待在环境变化后直接挂死占着终端谁也动不了。稳妥的写法是每一步都设超时超时就打印当前缓冲内容然后退出方便定位卡在哪一步。8. 我在这些年里攒下的几条实操体会关于分层这件事我最大的感受是不要试图一次记住所有细节而要建立症状到层的映射直觉。看到字符显示异常就想显示层看到行为异常就想执行层看到会话丢失就想复用层。有了这个直觉之后绝大多数问题都能在几百秒内锁定范围剩下的只是查文档的事。第二条是关于配置管理的。我的 Shell 配置文件现在分成了几块一块是最基础的环境变量只放 PATH 和语言编码这类必须的一块是交互式的便利设置别名和提示符都在这还有一块是外部工具引入的内容全部放在文件末尾并且加了注释说明来源。这样出问题时我只需要注释掉最后一块就能快速验证是不是插件引起的。第三条是关于先看再改。重命名、批量删除、改系统状态这类操作一定先把动作打印出来确认。echo mv ...这个习惯救过我不止一次。第四条是关于性能感知的。定期量一下 Shell 启动时间、提示符渲染耗时、常用命令的响应时间形成自己的基线。这样一旦某天开始变慢你立刻就能察觉而不是慢慢适应、最后想不起来它原来有多快。最后一条也是我觉得最有价值的遇到看不懂的报错先问自己在哪一层。是终端渲染错了是 Shell 解析错了还是程序本身执行出错了这个问题问出来答案往往就已经浮现一半了。
返回列表