很多人一提到 Linux Ubuntu 开发实用功能第一反应是把常用命令背下来。我刚开始也是这么干的抄了满满一页笔记结果真正用的时候还是挨个查。后来换了思路不追求命令数量而是把终端、监控、环境管理、故障恢复这些东西串成一套工作流效率反而提升了一大截。这篇总结适合长期在 Ubuntu 上写代码、部署、维护服务器的开发者尤其是不想折腾桌面但想提升命令行效率的人。内容不会贴一百个命令但会把我这两年实际用下来、踩过坑之后依然留在习惯里的功能挨个讲透。1. 终端体验改造从默认 bash 到 zsh tmux 的日常组合1.1 为什么值得把默认终端环境换掉Ubuntu 默认的 bash 其实并不差日常敲命令完全够用。但一旦你同时维护多个服务、经常通过 SSH 远程干活bash 的几个短板就会越来越明显没有可靠的会话恢复功能网络一断跑了一半的任务就跟着终端一起没了历史记录是一整块没法让不同项目窗口分开使用命令补全也比较基础敲一段路径或者记不清子命令的时候很累。我最初觉得 zsh 和 tmux 这两样是“锦上添花”直到有一次在服务器上跑一个耗时两小时的日志归并任务SSH 连接因为网络抖动断了任务直接消失。那之后我彻底转向了 tmux后来又把默认 shell 切到 zsh。现在回头看这两样东西解决的根本不是“好不好看”的问题而是“怎么让终端像开发工具一样可复用、可恢复”的问题。zsh 负责的是交互体验补全、别名、历史共享、更自由的提示符。tmux 负责的是会话管理它像给终端开了一扇门你随时可以离开房间回头还能原样推门进来。两者各干各的活不冲突。对 Ubuntu 来说安装很省事发行版的软件源里直接有。1.2 我实际保留下来的配置和使用方式安装就两条命令sudo apt update sudo apt install -y zsh tmux然后切换默认 shell执行chsh -s /usr/bin/zsh重新登录后生效。如果换过 shell 后提示找不到配置先执行command -v zsh确认路径再把 chsh 参数改对。我刚用 tmux 时最不习惯的是默认前缀键Ctrl-b和我常用的行首跳转冲突改成了Ctrl-a。在~/.tmux.conf里写set -g prefix C-a unbind C-b bind C-a send-prefix基础会话命令不用记太多tmux new -s work建一个叫 work 的会话tmux detach离开回来时用tmux attach -t work如果不记得名字tmux ls看列表。在会话里分屏我常用两个键Prefix加%是左右分屏加是上下分屏。一个窗口放日志一个窗口写命令比来回切省事得多。zsh 方面我没有用任何复杂的主题框架只加了几个习惯共享历史记录让两个终端窗口敲过的命令互通补全增强长目录名只敲开头就能补然后是一批高频别名。比如alias gsgit status alias gfgit fetch --prune alias lals -A alias dcdocker compose一个小经验如果之前在 bash 里积累了.bashrc里的别名切到 zsh 后不会自动读取要把内容迁到.zshrc里。很多朋友切完 shell 发现命令找不到多半就是这一步漏了。2. 系统监控与资源排查从“感觉卡了”到定位具体进程2.1 监控工具怎么选top、htop、glances 的实际差别很多人卡住的第一句话是“服务器最近有点慢”但“慢”是个模糊感受。从系统角度慢可以来自 CPU、内存、磁盘 IO、网络甚至某个进程在锁上死等。这时候手里要有能快速缩小范围的工具。top 是系统自带没有额外安装成本但输出排版比较原始按 CPU 排序还得敲快捷键很不直观。htop 是 top 的增强版显示彩色、支持树状进程视图、可以直接用方向键选择进程并发送信号适合日常上手。glances 又是一个层级它会同时展示 CPU、内存、磁盘、网络和进程列表还有 Web 输出模式适合盯一段时间的整体趋势。一个简单的选型建议场景推荐方式快速看一眼负载uptime、top交互式观察进程树htop想看磁盘和网络整体状态glances追一段时间的趋势glances --export csv或记录到日志内核级排查vmstat 1、iostat -x 1不要把监控工具当成神物重点是你要会看“高负载到底体现在哪一项”。我习惯先用uptime看 load average再用htop看进程排序最后用iostat确认是不是磁盘瓶颈。2.2 高负载排查的完整链路一次比较典型的排查是我接手的一台常驻开发机CPU 不高但整个系统交互很卡。负载三个数值都在 8 以上任务管理器里却没有明显的高 CPU 进程。这就是典型的“不是 CPU 忙而是进程在排队或者等 IO”。第一步看uptime理解 load average 的含义它代表一段时间内正在运行和等待运行的进程平均数。如果你的机器有 4 个核心负载长期超过 4就表示有任务在排队。生活化一点CPU 是厨师load average 是排队等菜的客人数量厨师再快排队人数太多也白搭。第二步用htop切到进程视图按 CPU 排序。如果 CPU 占用不高但负载很高别急着杀进程先查磁盘。用iostat -x 1看%util这个值接近 100% 时说明磁盘已经忙得抬不起头进程都在等 IO 完成。再进一步用iotop看是哪个进程在读写磁盘。如果%util不高但系统很卡再考虑是不是内存交换频繁。第三步看内存。free -h里不要只盯 free 那一列要看available它包含了可回收的缓存和剩余内存是更真实的可用值。内存不够时系统会走 swap一旦 swap 持续读写速度会断崖式下降。找到吃内存进程我常用ps aux --sort-%mem | head -20第四步是网络。端口和连接数异常时ss -tunap能同时看到监听端口和连接对端地址。比如有时候服务没起来但端口被别的内容占着很快就能定位。最后别忘了journalctl -u 服务名 --since 1 hour ago -f看服务级日志。很多所谓“莫名其妙卡顿”其实是某个服务在高频报错日志里全是异常记录。监控工具能告诉你现象日志才能告诉你原因。3. 开发环境管理版本切换、SSH 配置和容器使用的日常提速3.1 多版本命令切换系统自带的 alternatives 机制开发环境里最常见的一个烦恼是一台机器上装了好几个版本的运行环境系统默认调用的是旧版本手动改 PATH 又容易污染其他工具。如果用的是 Ubuntu 这类 Debian 系发行版可以先试系统自带的update-alternatives不用额外装东西。它的原理很简单系统里有很多命令名比如java、python、node这类真正执行的时候会通过一个符号链接指向当前默认版本。update-alternatives就是统一管理这些链接和优先级的小工具。举个我实际用过的例子。某台机器上装了多个 JDK分别放在/usr/lib/jvm/下的不同目录里。先注册sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk-17/bin/java 100 sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk-11/bin/java 80这里的数字就是要优先级数值大的默认优先。之后想临时换版本sudo update-alternatives --config java系统会列出所有已注册版本输入序号就能切换。好处是系统工具如果依赖某个固定路径切换后也不容易夹生。Python 这边我反而更推荐“系统自带的 python3 虚拟环境”的组合虚拟环境天然隔离不用折腾全局版本而且换机器后重建成本非常低。3.2 SSH Key 管理与登录配置开发机上最常用的远程连接方式就是 SSH。每次ssh 用户名地址再手动输密码确实能工作但一旦要维护几台机器、偶尔还要传文件就非常拖节奏。我建议先把密钥对和配置文件一次性弄好。生成密钥时现在的安全实践是优先用 ed25519ssh-keygen -t ed25519 -C youexample.com生成后把公钥送到目标机器ssh-copy-id 用户名目标地址这一步会把你本地的公钥追加到目标机的~/.ssh/authorized_keys里之后登录就不用输密码了。注意私钥文件权限必须是 600太开放会被 SSH 直接拒绝。机器多了之后建议把这些内容写进~/.ssh/config给每台机器起一个容易记忆的 Host 别名。比如Host codeserver HostName 198.51.100.23 User ubuntu Port 2222 IdentityFile ~/.ssh/id_ed25519 ServerAliveInterval 30这样以后只需要ssh codeserver就能登录不需要带完整参数。ServerAliveInterval 是防止长连接被中途掐掉的心跳参数远程跑任务时很实用。多个密钥需要同时使用时可以交给 SSH agent 管理启动后ssh-add添加私钥必要时配上ForwardAgent在跳板机上继续用本机密钥访问内网机器省去一堆拷贝公钥的操作。3.3 容器环境里常用的几个操作习惯现在开发环境几乎离不开容器。Ubuntu 上安装容器引擎后第一件事是用普通用户执行命令不用每次sudo。把用户加入对应用户组重新登录后生效sudo usermod -aG docker $USER不过要提一句加入这个用户组相当于给了用户很高的系统权限只适合在自己信任的机器上开启别为了省事在公司生产环境的机器上随手加。日常维护我常用三个维度状态、日志、清理。docker ps --filter statusrunning docker logs --tail 200 --follow 容器名 docker system prune -af --volumes第一条只看正在运行的容器第二条看最近的日志最后一条清理所有停止的容器、没用到的网络和匿名卷。prune后面的-a会把没有被容器引用的镜像也删掉所以跑之前最好确认镜像可以被重新拉取否则又要重新下载。另外我习惯把常驻开发服务注册成用户级 systemd 服务用systemctl --user管理这样能在开机后自动拉起崩溃时也能通过状态码和日志直接找原因。相比单纯依赖docker restartalways它有更清晰的启停职责也方便在容器之后补挂一些命令。4. 磁盘和文件操作定位大文件、软链接与日志瘦身4.1 在大目录里快速找出最占空间的内容磁盘用满这种事几乎每个人都遇到过。真正让人头疼的不是“满了”这个事实而是不知道哪些文件占了空间。很多人第一反应是du -sh *在小目录里还行一旦目录层级深、文件多跑起来会非常慢因为要扫描全部文件。我常用的两个组合一个是看某个目录下第一层各子目录的大小du -h --max-depth2 /home/某用户 2/dev/null | sort -hr | head -20--max-depth2控制扫描层级2/dev/null屏蔽没有权限访问的目录最后按大小倒序只取前 20 条。另一个是跨过小文件直接找大文件find /home -xdev -type f -size 500M -exec ls -lh {} \;-xdev的含义是不跨文件系统。为什么要加因为/home下很可能挂着其他数据盘或临时目录不加这个参数会把无关的挂载点也扫一遍浪费时间还可能碰到权限问题产生大量报错。使用du时有一个小技巧先定位到最可疑的一级目录比如/var或/home看那一层的子目录大小再逐层往深走。不要在一开始就用du -ah全盘扫描看起来威风实际上 CPU 和磁盘 IO 会被拖死机器上有别的任务时很容易互相干扰。4.2 软链接的几个常见坑软件项目里经常用软链接把代码目录、数据目录映射到其他位置比如/data盘空间大就把家目录下的项目目录链过去。ln -s /data/project ~/project一条命令就搞定但用起来有几个细节要注意。第一个坑是路径写法。创建软链接时如果使用相对路径链接目标会根据当前工作目录解析一旦从其他目录访问这个链接就可能失效。所以我的习惯是创建软链接时源路径一律写绝对路径。命令行里的realpath 路径能帮你把相对路径转成绝对路径。第二个坑是时代入目录的问题。很多构建工具会调用pwd获取当前目录但通过软链接进入的目录pwd默认返回链接所在路径不是真实路径。比如项目里生成临时文件的路径写死了构建脚本可能因此找不到文件。遇到这种问题先分清pwd -L和pwd -P前者显示逻辑路径后者显示物理真实路径。脚本里用到绝对路径时尽量取真实路径。第三个坑是权限。软链接本身有自己的文件权限但访问时要看的是源文件所在目录的权限。你把链接放在没有写权限的目录里就算源目录权限一切都对也可能写入失败。排查这类问题不要只盯着链接文件先把ls -ld两个目录都看一眼。4.3 给系统盘瘦身的三条命令开发机的系统盘总是会在某天突然告警。除了大文件最常见的元凶是日志、软件包缓存、系统包管理器自动带上的旧依赖。我的日常清理三板斧sudo apt clean sudo apt autoremove sudo journalctl --vacuum-size200Mapt clean清掉下载的软件包缓存apt autoremove移除不再被依赖的包journalctl --vacuum-size200M把系统日志总大小限制在 200M 以内超过的部分自动清除。对长期开发机来说这个限制不会影响排错因为最常见的日志都在近一两天之内太早的日志价值其实没那么大。如果用了自动更新机制旧内核也会越积越多。查看已安装内核时注意保留当前正在运行的内核版本不要为了省空间把正在用的内核删掉。方法很简单先用uname -r查看当前内核再去包管理器的已安装列表里手动删除旧内核。不确定的时候宁可不删空间问题可以通过上面几条清理命令缓解内核删错了反而更麻烦。5. 日常故障恢复启动、依赖与权限的几个高发问题5.1 系统启动卡住时的抢救思路开发机一旦启动卡住很多人会本能地强制关机这是最危险的动作。文件系统可能正处在写入状态反复断电容易造成损坏。正确做法是先用系统自带的恢复入口。Ubuntu 启动时在 GRUB 界面选择“高级选项”通常能进入恢复模式。在恢复模式的菜单里先看是否有文件系统检查选项如果检测到异常就让它跑一遍修复。修复时尽量卸载分区再执行fsck直接在挂载状态下强跑不太安全。如果连 GRUB 菜单都见不到思路就变成“用启动介质进入临时系统”。把系统镜像写到 U 盘后启动进入临时环境。先确认硬盘分区名再把根分区挂载到某个目录必要时把/dev、/proc、/sys等虚拟文件系统绑定挂载进去用 chroot 进入原本的系统环境再重新生成引导配置。重点提醒一句chroot 之前一定要备份重要数据这类操作在不同版本上差异很大不是死记命令就能复制的。我把这个流程当应急手册来用而不是日常操作。另一个很常见但容易被忽略的卡住场景其实是系统盘完全占满。很多服务在启动时尝试写日志磁盘没有任何剩余空间启动流程就一直卡在“等待写入完成”。遇到启动进度条长时间不动先想办法进恢复模式或者用启动介质挂载系统检查一下根分区可用空间往往比检查硬件更实际。5.2 依赖关系崩溃后的恢复顺序开发机上安装软件包时最常见的报错是“有损坏的软件包”或“依赖关系不满足”。我见过不少次因为一个包装到一半网络中断把整个包管理器的状态卡死了。遇到这种情况先别急着卸载软件也别搜索“把这几个库手动删掉”的操作。我的习惯是按顺序来sudo apt update sudo apt --fix-broken install sudo dpkg --configure -a第一行刷新软件包索引第二行让包管理器尝试修复依赖关系第三行把所有没有完成配置的包重新配置一遍。顺序不能反。如果某一步之后仍然报错再用dpkg -l看有没有残留的rc状态包这类包已经删除但配置文件还在一般不会影响系统运行但留着容易让后续操作弹警告。最怕的是看到“需要卸载某依赖”就冲动地手动删包。Ubuntu 的包依赖关系很密集一个库可能是十几个软件的共同依赖拍脑袋删掉后果可能比原来的错误更严重。任何修复动作跑完后记得用apt autoremove清理一次不要在这台机器上留着半坏的包状态。5.3 sudoers 权限混乱后的自救有一次我手动改/etc/sudoers文件写错语法保存后系统直接拒绝所有sudo操作。那一刻确实会慌因为连修复权限的入口都被堵住了。正确且最快的自救方式是重启进入恢复模式的 root shell。如果根分区是只读挂载先执行mount -o remount,rw /然后检查/etc/sudoers。修改这个文件时不要用普通编辑器直接乱改用visudo工具它会在保存前做语法校验避免再次把自己锁在门外。另一个容易踩的坑是权限。/etc/sudoers的权限应该是所有者可读可写其他用户只读尤其不能给普通用户写权限。我在恢复模式里第一件事就是确认权限没有变成 777再用chmod 440 /etc/sudoers修正。正常情况下这个文件不会频繁变动更不要把用户直接加进 root 组来绕开 sudoers那样虽然能临时解决问题但会让整个系统的权限边界变得形同虚设。6. 自动化脚本封装把重复操作变成一条命令6.1 用 shell 函数替代每天重复的初始化动作开发工作里有很多“每天重复”的动作新建项目、初始化目录、配置虚拟环境、建 git 仓库、打开编辑器。这种循环机械操作至少十分钟一次用脚本函数化之后效率提升非常明显。我习惯把这类函数写进 shell 配置里比如在.zshrc中定义一个创建前端项目的函数mknproj() { local project_dir$1 if [[ -z $project_dir ]]; then echo 用法: mknproj 项目名 return 1 fi mkdir -p $project_dir/src cd $project_dir git init echo # ${project_dir} README.md }函数体里做了三件事检查参数是否为空创建目录结构并进入初始化 git 仓库。为什么强调变量加引号因为项目名可能包含空格或特殊字符不加引号会被 shell 解析成多个词目录就建错地方了。如果需要更严谨的脚本逻辑我建议把可复用的部分抽出来放到独立脚本文件而不是全堆在配置文件里。脚本开头写下#!/usr/bin/env bash set -euo pipefailset -e表示遇到错误就退出-u表示使用未定义变量时报错-o pipefail让管道命令失败时整个管道返回非零状态。这三项是脚本工程化的地基能避免大多数“看起来执行了但结果不对”的怪问题。6.2 用 systemd 用户定时器替代简单 cron很多人一想到定时任务就是 crontab但对于用户级的开发任务我更喜欢 systemd 的用户级 timer。它在启动依赖、失败记录和日志查看上都更透明而且不需要后台常驻脚本写起来也不复杂。先定义服务文件~/.config/systemd/user/backup.service[Unit] DescriptionBackup development directory snapshot [Service] Typeoneshot ExecStart/home/myuser/bin/backup-snapshot.sh再定义对应的 timer 文件[Unit] DescriptionRun backup snapshot every day at 02:00 [Timer] OnCalendar*-*-* 02:00:00 Persistenttrue [Install] WantedBydefault.target启用并立即观察状态systemctl --user daemon-reload systemctl --user enable --now backup.timer systemctl --user list-timersPersistenttrue的意思是如果定时时间点机器关机了等下次开机后立即补一次任务。这个特性是普通 crontab 默认不具备的。每次跑完任务用systemctl --user status backup.service就能看到退出码和最近日志排错比翻 cron 的邮件输出舒服很多。6.3 把故障排查也变成一条命令当系统出现异常时我会先花几十秒收集基础信息。以前是手打一长串命令后来写了一个小脚本把高频排查信息集中输出#!/usr/bin/env bash set -euo pipefail echo 系统时间与运行时长 uptime echo 磁盘使用 df -hT -x tmpfs -x devtmpfs echo 内存概览 free -h echo 负载进程前五 ps aux --sort-%cpu | head -6 echo 近期错误日志 journalctl -p err --since 1 hour ago --no-pager | tail -40脚本放到/usr/local/bin/sysreport执行chmod x后遇到异常先跑一次输出的内容足够覆盖 80% 的初步判断。这个脚本不需要 root 权限只读信息很安全。我还会把常用服务的状态检查追加进去这样每次排查都有一份固定格式的“系统快照”和别人沟通时也方便描述。写这类脚本时最重要的一点是不要把所有机器都当成同一套环境。不同机器上可能缺少某些命令脚本运行时如果某个命令不存在会直接报错退出。用command -v 命令名 /dev/null先判断一下或者至少在使用非常规工具前给出明确提示能让脚本在不同机器之间迁移省很多事。
阅读完成 · 觉得有帮助?