‹ Term

常见问题

使用 Term 时最常遇到的问题和解决办法。

01为什么会断

会话为什么会断开?

通常有两个原因。一个是切到后台。切走时,Term 会向系统申请一小段后台时间,最长 90 秒,在这段时间内回来,会话原封不动,接个电话、回条消息都不会断线。系统实际给的额度可能更短(它自己的上限约 3 分钟,低电量时约 1 分钟)。超过这段时间之后,为了省电,HarmonyOS 会挂起长时间停留在后台的应用,连接随之关闭:SSH 会话断开,里面正在运行的程序也会停止。

另一个原因是网络切换了,这一种连接会直接断开。普通 SSH(telnet 也一样)是一条 TCP 连接,两端各钉死在一个 IP 地址上:手机从 Wi-Fi 切到 5G、走出路由器的覆盖范围、进电梯或者过隧道,地址一变、或者包发不出去,这条连接就没了,哪怕你全程都看着它。而且往往没有任何提示,直到你敲下一个键,发现没有回显。mosh 是唯一扛得住这个的传输方式:它走 UDP,认的是会话而不是地址,换到新网络上会自己接着跑。

下面是四种办法。前三种是让会话不断:从最简单到最稳健;第四种是万一还是断了,怎么回到原来的地方。

02保持在线的四种办法

方法一让 Term 一直待在台前

只要 Term 在屏幕上,会话就一直是活的(适合稳定的网络环境)。把它放进悬浮窗或分屏,它就算在前台:你可以同时用别的应用而不掉线。

  1. 悬浮窗:从屏幕底部往上滑,停一下,进入最近任务界面;点 Term 卡片上的悬浮窗图标,终端会缩成一个可拖动的小窗。
  2. 分屏:同样在最近任务界面,点 Term 卡片上的分屏图标,再选择另一个应用并排显示。
  3. 挂着会话去查资料、回消息,回来继续敲:连接从未断开。

方法二Mosh + tmux/zellij(最稳)推荐

最稳的做法是:服务器上跑 tmux 或 zellij,连接用 mosh。mosh 能扛住网络切换并自动重连(适合一周或者更久的挂机查看),复用器则让程序在服务器上持续运行:随时断开、随时回来,原地接着干。

  1. 在服务器上安装 tmux:sudo apt install tmux(Debian/Ubuntu)或 sudo dnf install tmux(Fedora)。
  2. 用 Term 连上主机,新建一个命名会话:tmux new -s work。
  3. 在里面正常工作:即使手机锁屏、切后台、断网,会话都留在服务器上。
  4. 重新连接后挂回去:tmux attach -t work,一切保持原样。
  5. 更进一步:新建主机时把连接方式选为 mosh(服务器需安装 mosh-server),漫游切换 Wi-Fi / 蜂窝也不断线。
tmux — 服务器上
# 新建命名会话
tmux new -s work

# 重连之后挂回去
tmux attach -t work

默认配置的 tmux 会悄悄吞掉不少 Term 能做的事(内嵌图片、剪贴板、带修饰键的组合键),而且它不会报错,只是没反应。所以值得配一次。我们用来测试 Term 的那份配置有专门一页,每一行都有注释,还有一键复制整份的按钮。其中有一行正是为这一节写的:tmux 发剪贴板 OSC 52 时不填目标,而 mosh 只转发 52;c; 这一种写法,没有那行 terminal-overrides 去纠正,你在 tmux 里复制的内容只会落进 tmux 自己的粘贴缓冲区,根本出不了服务器。其中两行跟图片有关的设置,本页下面还有一节专门讲。

逐行讲这份配置:我们在用的 tmux 配置

方法三把长任务丢到后台跑

耗时任务别挂在终端前台上跑:丢进 tmux/zellij,或者用 nohup … &。即使应用断连,任务也会在服务器上跑完,之后重连回来检查结果即可。

nohup — 服务器上
# 脱离终端后台运行,输出写入 out.log
nohup ./long-task.sh > out.log 2>&1 &

# 重连后查看进度
tail -f out.log

方法四断了也没事,Term 帮你连回去

v26.1.0+

前面三种都是尽量不让会话断。这一种承认它总有断的时候(手机被挂起、网络断了),但仍然把你送回原来的工作现场。给主机设置一条启动脚本,让它接上多路复用器,脚本下方就会出现自动恢复会话开关:打开之后,随应用一起被杀掉的标签页会在下次启动时重新打开,在后台待太久而被断开的标签页会在你切回来时自动重连。

这里没有任何东西被“复活”,这一点值得说清楚:SSH 连接一旦断开就是断了,服务器那边没有会话在等你。Term 做的是重新连接,真正保住你工作的是多路复用器,启动脚本负责把你重新放回它里面。所以只有当脚本确实会接上点什么,开关才会出现;只对 SSH 提供,默认关闭,而你自己关掉的标签页不会被找回来。

  1. 服务器上先装好 tmux 或 zellij,并想好要常驻的会话名:下面用的是 main。
  2. 编辑主机 → 高级选项 → 启动脚本,填一条会接入会话的命令:tmux 用 tmux new -A -s main(没有就新建、有就接上),zellij 用对应的 attach 命令。第一次连接和之后每一次都用同一行。
  3. 字段正下方会出现自动恢复会话开关,打开并保存。
  4. 就这些。开着标签页把应用杀掉,下次启动它会回来;把 Term 丢在后台直到连接断掉,切回来时它会自己重连。
可以触发开关的启动脚本
# tmux:没有就新建,有就接上(zellij 用户用对应 attach 命令)
tmux new -A -s main

# zellij
zellij attach -c main

想了解更多:会自己回来的 SSH 会话:什么能恢复、什么不能

03tmux

在手机上怎么用 tmux?

建一个命名会话,Term 就会发现你在跑 tmux,然后把软键盘上方的按键栏换成一套 tmux 专用键:esc、^B 前缀、四个方向键,以及一批一按就发出去的前缀组合。识别靠的是 Shell 集成上报的命令(默认开启),所以别把它关掉。

  1. 连上主机,新建命名会话:tmux new -s work。
  2. 按键栏随即换成 tmux 键集:^B[ 进入复制模式(回看历史就靠它)、^Bz 放大当前窗格、^B% 与 ^B" 分屏、^Bc 新建窗口、^Bn / ^Bp 切换下一个 / 上一个窗口、^Bw 窗口列表、^Bd 脱离;^C 和 ^R 也在,毕竟底下还是个 shell 提示符。
  3. 这一套是内置的,跟着前台程序自动切换,不需要你配置什么。需要别的控制键时,点一下按键栏最左边的 ctrl:它会为下一个按键挂上 Ctrl 修饰(alt、shift 同理)。
  4. 离开时按 ^Bd 脱离:会话和里面正在跑的程序都留在服务器上。
  5. 下次连上来 tmux attach -t work 挂回去,一切保持原样。
一个 tmux 会话上下分成两个窗格:上面运行 htop,下面是 tail 打出的日志;最底部是 tmux 自己的状态栏。
tmux 分屏,上下两个窗格:htop 和 tail -12 ~/logs/app.log;最下面那条深色行是 tmux 自己的状态栏,左边是会话名 work,中间是窗口 1:bash*,右边是时钟。顶部导航栏写着的 tmux new -s work 就是 Shell 集成上报的命令,按键栏正是据此切到 tmux 键集的。
终端里的两行:tmux 打出的 [detached (from session work)],下一行是已经输入 tmux attach -t work 的提示符。
脱离,再挂回去:按下 ^Bd 之后,tmux 打出 [detached (from session work)] 并把 shell 还给你;下一行的 tmux attach -t work 会把整个会话原样接回来。
04mosh

怎么用 mosh 连?

先在服务器上装好 mosh,Term 实际需要的是里面的 mosh-server。然后编辑主机,把连接方式选成 Mosh;应用里那行提示已经把话说清楚了:登录仍然走 SSH,之后才把会话交给 UDP。所以服务器一侧还要放行 UDP 60000–61000;如果只开放了少数几个 UDP 端口,也可以在主机编辑页把 mosh 钉在这几个端口上。连上之后,切换 Wi-Fi / 蜂窝、信号中断都不会掉线,会话会自己重连,上面那条后台挂起的规矩,对 mosh 不适用。

  1. 在服务器上安装:mosh 各家发行版基本都打了包,通常一行就够;下面代码块里按系统列了对应的命令,也包括 macOS。RHEL、Rocky、AlmaLinux 上它在 EPEL 里,得先启用 EPEL。
  2. 在 Term 里编辑这台主机 → 连接方式 → 选 Mosh。主机、端口、用户名照旧填 SSH 那一套,登录本来就还是走 SSH。
  3. 放行服务器(以及中间链路)上的 UDP 60000–61000,不特别指定的话,mosh 会在这个范围里自己挑一个端口。只放行了特定端口的话,就在连接方式里的「UDP 端口或范围(可选)」填上:单个端口(60000)或一段范围(60000:61000);留空就是自动挑。
  4. 想确认真的走在 mosh 上:连上后跑 ps -o args= -p $PPID,shell 的父进程应该是 mosh-server,而不是 sshd。
主机编辑页的「连接方式」卡片,分段控件 SSH / Mosh / Telnet 中 Mosh 处于选中状态。
连接方式 · Mosh,主机编辑页里的分段控件(SSH / Mosh / Telnet),选中 Mosh。上面「服务器」卡片里的主机、端口、用户名照旧是 SSH 那一套。下面那行是应用自己的提示:主机需要装 mosh-server,登录仍通过 SSH 完成。
mosh — 服务器上
# 安装 mosh:Term 要的是里面的 mosh-server
sudo apt install mosh     # Debian / Ubuntu
sudo dnf install mosh     # Fedora / RHEL
sudo pacman -S mosh       # Arch
sudo zypper install mosh  # openSUSE
sudo apk add mosh         # Alpine
brew install mosh         # macOS (Homebrew)
sudo pkg install mosh     # FreeBSD

# 确认 shell 的父进程真的是 mosh-server
ps -o args= -p $PPID
一个 mosh 会话里,ps -o args= -p $PPID 的输出是 mosh-server new -s -c 256 -l LANG=en_US.UTF-8。
确认走的是 mosh,ps -o args= -p $PPID 回答的是 mosh-server new -s -c 256 …:shell 的父进程是 mosh-server,不是 sshd,会话确实跑在 UDP 上。顶部导航栏也写着 Sandbox (mosh)。

想再深一步:Mosh 进阶设置:端口、会话超时、初始化脚本

mosh 会话为什么不能往上翻?

因为 mosh 是直接重绘屏幕,而不是逐行传输,根本没有历史可以给 Term 往上翻。这是 mosh 的设计,不是 Term 的限制。想回看,就在服务器上跑 tmux 或 zellij,用它自己的回滚:在 tmux 里就是复制模式,也就是按键栏上的 ^B[(见上面 tmux 那一节)。应用里也会在原地说同一件事,首次连上 mosh 时的提示卡,以及在 mosh 会话里第一次上滑时弹出的提示。

关掉 mosh 标签页,服务器上的会话还在吗?

会。关掉 mosh 标签页时,Term 会发出 mosh 的关闭数据包,结束服务器上的那个会话;万一这些包没能送到,它还会再用 SSH 连回去,把遗留的 mosh-server 收掉,基本不会在主机上留下孤儿进程。想稍后回来,就别关标签页。如果这份工作必须活得比标签页更久,那正是 tmux/zellij 的用处:在 mosh 会话里跑 tmux/zellij,关掉标签页也只是脱离而已。

「mosh-server 未启动」/「UDP 会话未能建立」

mosh 先用 SSH 登录,再把会话交给 UDP,这两条报错分别对应其中一段出了问题,Term 会直接告诉你是哪一段。失败画面下方保留着原始错误和它的 E_ 代码(提 bug 时用得上),并给出编辑主机和重试两个按钮。

  1. 已连接,但 mosh-server 未启动。 登录那一步是成功的,缺的是程序本身:在服务器上装 mosh,或者把这台主机改回 SSH。
  2. 已通过 SSH 连接,但 mosh 的 UDP 会话未能建立。 mosh-server 起来了,数据包却没到:放行服务器以及中间链路上的 UDP 60000–61000。

tmux 里能显示图片吗?

能,但 tmux.conf 里要加两行。Term 原生解码 sixel、kitty 图形协议和 iTerm2 的 imgcat(OSC 1337)(PNG、JPEG 以及 GIF 的第一帧),并把图像合成在文字下层。要穿过 tmux,图像走的是 kitty 的 Unicode 占位符方案:图像 id 藏在单元格的真彩色前景色里。所以 tmux 一要放行这段转义序列,二要在内部保持真彩色,少了第二条,tmux 会把带 id 的颜色降到 256 色,id 被改坏,图也就废了。这两行都需要 tmux 3.3a 或更新的版本,版本太旧的话,tmux 启动时只会报 “unknown option”,图片就悄无声息地出不来了。

~/.tmux.conf
# 放行图像的转义序列
set -g allow-passthrough on

# 在 tmux 内保持真彩色,否则图像 id 会被改坏
set -as terminal-features ",*:RGB"
05参考

备注与参考

mosh 会话不受影响,它本来就能在后台存活并自动重连,所以后台警告对它不适用。应用内也有同样的说明:主机页面的后台会话警告横幅 → 点开「保持会话在线」帮助页。

参考: mosh.org · tmux 入门 · zellij · GNU screen · nohup(1)