不管是刚接触 Linux 的新手,还是已经在生产环境里摸爬滚打多年的运维,日常都会遇到几个"说大不大、说小不小"的别扭问题:明明删了文件,磁盘空间却不见少;从 Windows 那边拷过来的压缩包一解压全是乱码;外接显示器插上之后黑屏没反应;新建的用户怎么都配不对 sudo 权限。这些问题的共同点是,网上搜出来的答案往往只给一个命令,不讲为什么,换个环境就失灵。这篇内容我不打算再罗列"Linux 常用 100 个命令"那种清单,而是挑几个高频出现、又确实容易卡壳的真实场景,把背后的原理和完整的排查思路拆开讲清楚。无论你用的是虚拟机里的 Ubuntu、物理机上的 CentOS,还是 Windows 下的 WSL,这几套思路基本都能直接套用。
1. WSL 里删除文件后磁盘空间没释放:虚拟磁盘的"只进不出"
如果你在 WSL 里用 rm 删掉几个大文件,回到 Windows 一看,C 盘可用空间一点没变多,别急着怀疑人生,这属于 WSL 的常见表现,不是删除失败。
1.1 问题根源:ext4.vhdx 只增长不收缩
WSL 1 和 WSL 2 的磁盘存储机制不一样。WSL 2 跑在轻量级虚拟机里,整个 Linux 文件系统被打包进一个虚拟磁盘文件,默认位置在:
code复制C:\Users\你的用户名\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu_xxxx\LocalState\ext4.vhdx
这个 ext4.vhdx 的本质是一个动态扩展的虚拟硬盘。你在 WSL 里写文件时,它会像气球一样逐渐变大;但当你删除 WSL 内部的文件时,这个虚拟磁盘文件并不会自动缩小。这就好比一个箱子装满了东西,你把东西拿出去了,箱子本身并不会自己变小,除非你手动把它压扁。
所以排查的第一步不是去看 Windows 的资源管理器,而是在 WSL 内部确认数据是否真的删干净了:
bash复制# 查看当前磁盘占用情况
df -h /
# 找出占用空间的大目录,按从大到小排列
sudo du -x -h --max-depth=1 / 2>/dev/null | sort -hr | head -20
df -h 看到的是文件系统层面的使用率,如果这里显示的 Avail 空间已经变大,说明文件确实删掉了,剩下的事情就是怎么让虚拟磁盘文件也瘦身。
1.2 解决方案:从 WSL 外部压缩虚拟磁盘
给 ext4.vhdx 瘦身最有效的方法是先彻底关闭 WSL,再用 Windows 自带的磁盘工具去压缩虚拟磁盘文件。
第一步,在 PowerShell(管理员模式)里完整关闭 WSL:
powershell复制wsl --shutdown
注意,wsl --shutdown 会把所有正在运行的 WSL 发行版全部关停。执行完这条命令后,建议等几秒钟,确保 Vmmem 和 VmmemWSL 进程真正退出。
第二步,用 diskpart 压缩虚拟磁盘。打开一个新的 PowerShell(管理员)窗口,依次执行:
powershell复制diskpart
select vdisk file="C:\Users\你的用户名\AppData\Local\Packages\CanonicalGroupLimited.Ubuntu_xxxx\LocalState\ext4.vhdx"
attach vdisk readonly
compact vdisk
detach vdisk
exit
attach vdisk readonly 这一步很关键,它把虚拟磁盘以只读方式挂载,避免压缩过程中产生新的写入。compact vdisk 会扫描虚拟磁盘里未使用的空间并释放掉,压缩完成后 detach vdisk 解除挂载。
第三步,重新进入 WSL,确认 / 的可用空间正常,同时回到 Windows 看 C 盘空间是否回升。实测下来,删掉几十 GB 的构建缓存后,压缩完 ext4.vhdx 基本能瘦回和真实使用量接近的大小。
1.3 后续如何避免再被这个问题坑
既然知道了 WSL 的虚拟磁盘"只进不出",平时使用时就该养成几个习惯:
- 大文件、编译中间产物、Docker 镜像缓存尽量放到
/tmp或者一个固定目录,确认不需要了尽快清掉,不要堆积。 - 用
wsl --manage <发行版名> --set-sparse true将磁盘设为稀疏模式。稀疏模式的虚拟磁盘在 Linux 内删除文件后,空间会更容易归还给 Windows,不用每次手动 compact。 - 不要频繁在 WSL 和 Windows 之间拷贝超大文件,跨文件系统的读写开销远高于同一文件系统内部的操作。
code复制wsl --manage Ubuntu --set-sparse true
要注意,--set-sparse true 需要较新版本的 WSL,如果提示命令不存在,先运行 wsl --update 升级到最新版本。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. ZIP 压缩包解压中文乱码:编码问题的源头与三种解法
"Linux 解压文件乱码"在热搜里出现频率相当高,尤其是从 Windows 或国产软件生态里导出的 ZIP 压缩包。明明在 Windows 上压缩时文件名显示正常,传到 Linux 一解压就成了 ����.pdf 这种乱码,看着非常头疼。
2.1 为什么同一个 ZIP 文件在 Windows 和 Linux 里表现不一样
根源在于文件名编码方式不一致。Windows 中文版的压缩软件在创建 ZIP 时,对文件名的编码默认使用本地编码,也就是 GBK/GB18030;而 Linux 下绝大多数现代工具默认使用 UTF-8。ZIP 格式本身没有硬性规定文件名必须用哪种编码,很多压缩工具在写入文件名时也没有声明编码信息,解压时如果猜不对,就会把 GBK 的字节流按 UTF-8 去解码,乱码就出现了。
这就好比两个人都说中文,但一个把字写成了简体,另一个习惯读繁体,如果不做转换,双方都看不懂对方写的是什么。技术术语叫"编码不匹配"。
2.2 解法一:用 unzip 指定编码
如果你的系统里已经装了 unzip,最直接的方式是在解压时告诉它文件名用的什么编码:
bash复制# 解压前先列出压缩包内容,确认是否乱码
unzip -l 测试.zip
# 如果中文文件名是 GBK 编码,用 -O 参数指定
unzip -O CP936 测试.zip
CP936 是 Windows 中文版使用的代码页编号,和 GBK 基本等价。不同发行版对 unzip -O 的支持不一样,有些精简版 unzip 可能不认这个参数。如果提示 invalid option -- O,可以改用下面的方案。
2.3 解法二:用 7z 加编码转换参数
p7zip 家族里的 7z 命令对编码的处理比 unzip 灵活一些。解压时用:
bash复制# 先安装 7zip(Debian/Ubuntu 系)
sudo apt install p7zip-full
# 解压并指定文件名编码
7z x 测试.zip -o输出目录 -scsGBK
-scsGBK 的意思是让 7z 把压缩包内的文件名按 GBK 解析。如果你的压缩包文件名是 UTF-8,就用 -scsUTF-8。实际使用中,-scs 参数并不是总能完美解决问题,因为部分压缩包混合了多种编码,或者压根没按规范写,这时候可以试试第三种方法。
2.4 解法三:使用 unar 自动检测编码
unar 是我个人最推荐的工具,它对中文文件名乱码的处理能力要比 unzip 和 7z 都省心。unar 会自动尝试检测压缩包内文件名编码,然后正确转换到当前系统编码:
bash复制# 安装 unar(macOS 和 Linux 都有对应包)
sudo apt install unar
# 直接解压,它会自动检测编码
unar 测试.zip
实测下来,unar 对 GBK、Big5、Shift-JIS 等编码的识别率都很不错,解压完的文件名能正常显示成中文。如果你经常需要处理跨平台的压缩包,这工具值得常驻系统。
2.5 解压 7z 格式的补充技巧
热搜词里还有一条"linux 解压 7z 文件"。7z 格式本身的编码信息记录得比 ZIP 更规范,出现中文乱码的概率相对低一些。核心操作就是先装 p7zip-full,然后:
bash复制# 解压 7z 文件
7z x 目标.7z -o/自定义输出目录
如果你只是临时解压一个文件而不想每次都敲 -o 参数,直接 7z x 目标.7z 会把内容解压到当前目录,但这样容易把压缩包里的文件散落得到处都是,建议还是养成指定输出目录的习惯。
3. 外接显示器无画面:图形栈的排查链路
"linux 外接显示器无画面"这个热搜词一看就是笔记本用户插了 HDMI 或者 DP 线之后,外接屏幕完全没反应或者显示"无信号"。这种情况在 Windows 下一般是自动识别,但到了 Linux 上,由于桌面环境、显卡驱动和显示协议各不相同,问题排查链路会更加复杂。
3.1 先判断显卡驱动加载情况
外接显示器没画面,很大概率不是屏幕坏了,而是显卡驱动没有正确加载。在终端里先看当前系统实际使用的显卡驱动:
bash复制# 列出所有显示相关设备及其驱动状态
lspci -k | grep -A 3 -E "VGA|3D|Display"
# 查看当前内核加载的显卡驱动模块
lsmod | grep -E "i915|amdgpu|nvidia"
如果笔记本是双显卡(Intel 核显 + NVIDIA 独显),需要特别留意 NVIDIA 驱动是否成功加载。一台使用 NVIDIA 独显的笔记本,如果安装的是开源的 nouveau 驱动,外接显示器的输出往往不稳定甚至完全不工作。这时需要先禁用 nouveau,再安装 NVIDIA 官方闭源驱动:
bash复制# 查看当前 Xorg 日志中与驱动程序相关的报错
grep -i "ee\|error\|failed" /var/log/Xorg.0.log | head -30
3.2 用 xrandr 检查显示器识别情况
驱动正常加载后,下一步是检查系统到底有没有"看到"外接显示器。在终端运行:
bash复制xrandr
输出结果里 connected 的显示设备就是被系统识别到的,disconnected 的表示当前没有接上或信号未被感知。如果 HDMI 线已经插好但这里还是显示 disconnected,优先检查线材和接口,其次检查驱动。如果显示 connected 但它后面标注 primary 或分辨率异常,可以尝试手动指定分辨率:
bash复制# 查看所有可用分辨率模式
xrandr
# 手动指定外接显示器的输出分辨率
xrandr --output HDMI-1-1 --mode 1920x1080 --rate 60
要注意,HDMI-1-1 这个名字需要从 xrandr 的输出里实际读取,不同硬件和驱动下名字可能完全不同。
3.3 Wayland 与 X11 的兼容性陷阱
如果你用的是较新的桌面发行版,默认可能跑在 Wayland 会话上。Wayland 对多显示器的支持已经比较成熟,但如果外接的是老款显示器或者转接头(比如 HDMI 转 VGA),Wayland 下偶尔会识别不到。最简单的验证方式:在登录界面切换到 Xorg 会话试试。
Ubuntu 系在 GDM 登录界面点击用户名后,右下角或右上角有一个齿轮图标,可以在 "Ubuntu on Wayland" 和 "Ubuntu on Xorg" 之间切换。切换到 Xorg 后如果外接显示器恢复正常,说明问题出在 Wayland 对特定硬件组合的支持上,而不是硬件损坏。
3.4 一个经常被忽略的细节:显卡驱动冲突
如果你之前为了某个应用安装过不同版本的显卡驱动,或者系统里同时存在 NVIDIA 驱动的多个版本,dkms status 可以帮你确认当前实际生效的内核模块:
bash复制dkms status
如果看到某个驱动模块后面有 installed 但同时存在 broken 之类的状态,说明驱动编译或安装过程中出了问题。这种情况最省心的处理方式是把旧的驱动模块全部移除,重新安装当前内核版本对应的驱动,然后重启。
code复制sudo apt purge *nvidia*
sudo ubuntu-drivers autoinstall
sudo reboot
ubuntu-drivers autoinstall 会自动选择合适的 NVIDIA 驱动版本,并处理依赖问题。很多外接显示器黑屏的案例,最后就是用这个组合命令解决的。
4. 新建用户与 sudo 权限分配:不要直接 edit /etc/sudoers
热搜词里有"linux 新建用户"和"linux 提权",我猜很多人搜这些关键词是在配置测试环境或者准备面试。这里我想专门讲清楚一件事:如何正确地新建用户并授予合理的 sudo 权限,以及为什么不要直接上手编辑 /etc/sudoers。
4.1 useradd 和 adduser 的区别
useradd 和 adduser 看起来就差一个字母,实际完全是两回事:
| 命令 | 类型 | 特点 |
|---|---|---|
useradd |
底层命令 | 参数复杂,创建用户后默认不创建家目录、不设置密码、不建用户组 |
adduser |
交互式 Perl 脚本 | 自动化完成创建家目录、生成用户组、设置密码等一系列操作 |
对绝大多数场景,直接用 adduser 更省心:
bash复制sudo adduser zhangsan
执行后会交互式地问你设置密码、确认用户信息,整个过程非常流畅。用 useradd 则必须自己补齐参数:
bash复制sudo useradd -m -s /bin/bash zhangsan
sudo passwd zhangsan
-m 表示创建家目录,-s /bin/bash 指定默认 Shell,不写的话可能默认成 sh,登录体验会非常奇怪。
4.2 sudo 权限:用 visudo,不是 vim /etc/sudoers
给用户授权 sudo 有很多方式,常见的是把用户加入 sudo 组:
bash复制sudo usermod -aG sudo zhangsan
但如果你需要为某个用户配置细粒度的 sudo 规则,比如只能执行某些命令、不需要密码等,就必须修改 /etc/sudoers。这里必须强调:永远不要直接用 vim 去编辑 /etc/sudoers。因为 /etc/sudoers 语法错误会直接导致 sudo 不可用,如果用了 visudo,它会在保存前做语法校验,避免你把自己锁在门外。
bash复制sudo visudo
在文件末尾追加:
code复制zhangsan ALL=(ALL:ALL) ALL
zhangsan ALL=(ALL) NOPASSWD:/usr/bin/systemctl restart nginx
第二行的含义是:用户 zhangsan 在任何主机上,可以免密码执行 systemctl restart nginx 这一条命令。NOPASSWD 后面跟的是命令的绝对路径,多个命令用逗号分隔。这种配置在需要给开发人员有限运维权限时非常有用,比直接给全部 sudo 要安全得多。
如果哪天你手滑配置错了,导致 sudo 完全用不了,可以进 recovery 模式或者用 Live CD 启动,挂载根分区后手动修正 /etc/sudoers 文件。所以配置前给 /etc/sudoers 留一个备份也是好习惯:
bash复制sudo cp /etc/sudoers /etc/sudoers.bak.$(date +%F)
4.3 用户组设计:比逐个授权更高效的做法
当用户数量多起来之后,逐个给用户配 sudo 规则会非常凌乱。更合理的方案是根据角色建组,然后把用户的 sudo 权限配置到组级别。比如:
bash复制sudo groupadd devops
sudo usermod -aG devops zhangsan
然后在 /etc/sudoers 里配置组规则:
code复制%devops ALL=(ALL) NOPASSWD:/usr/bin/docker,/usr/bin/git
%devops 前面的 % 表示这是一个用户组。这样一来,新同事入职时只需要把他加入 devops 组,权限就自动对齐了;离职时把他移出组,所有相关权限一次性回收。这个思路在做 Linux 服务器权限治理时非常实用。
4.4 关于提权这件事,说点更"安全"的理解
热搜词里有"linux 提权",我理解大多数人是想了解 Linux 权限提升的机制,从而更好地理解系统的权限隔离。从一个负责任的使用者角度来看,比起去网上找各种提权漏洞和攻击手法,更有价值的是了解系统权限模型的边界:root 是内核级权限,普通用户通过 sudo 获取的是受控提权,setuid 位则会让二进制文件以文件属主身份运行。理解了这些,你才会知道为什么不要把脚本挂在 setuid 上,为什么不要让普通用户有写 /etc 的权限,为什么运行未知二进制前要检查它的权限位。
code复制ls -l /usr/bin/passwd
# 会看到 -rwsr-xr-x,这个 s 就是 setuid 位
这个权限位也是很多经典提权漏洞的根源。安全的核心不是追求"能执行多少提权命令",而是懂得如何在授权范围内做正确的事。
5. JDK、Python 和常用软件的安装路线:包管理器优先原则
热搜词里的"linux系统安装python""linux安装jdk""linux安装libreoffice"都属于同一种场景:想在 Linux 上装一个软件。很多人习惯先去官网下载压缩包,然后手动解压、配置环境变量,这一套流程繁琐且容易出错。实际上大部分情况下,包管理器能帮你完成所有事。
5.1 为什么先选包管理器而不是源码编译
Linux 软件安装有三条主流路线:包管理器、官方二进制包、源码编译。优先用包管理器的核心原因是它负责处理依赖关系。举个例子,你想装 libreoffice,如果自己手动装,需要先确认所有依赖库版本已安装,缺一个就启动失败;用 apt 安装则把这些麻烦事全接管了。而且包管理器安装的软件有统一的卸载、升级入口,出问题时排查成本低很多。
Debian/Ubuntu 系用 apt,CentOS/RHEL/Fedora 系用 dnf,Arch/Manjaro 系用 pacman。先确认自己属于哪个系,再动手。
bash复制# Debian/Ubuntu
sudo apt update && sudo apt upgrade
sudo apt install libreoffice
# CentOS/RHEL 8+
sudo dnf install libreoffice
5.2 安装 Python:别动系统自带的 Python
在 Linux 上安装 Python 时,最容易踩的坑是覆盖了系统自带的 Python。Ubuntu、Debian 等发行版内部很多系统工具依赖系统自带的 Python,比如 apt 的某些扩展、Ubuntu 的软件中心等。如果你手动编译装了个新版 Python 并直接把 /usr/bin/python3 指向它,很可能会导致部分系统工具崩溃。
更稳妥的做法是使用 apt 安装官方仓库里的 Python,或者用 pyenv 做多版本管理。如果你只是需要一个能用就行,最省事的方式是:
bash复制sudo apt install python3 python3-pip
如果对版本有要求(比如必须要 Python 3.12),可以用 deadsnakes PPA(Ubuntu 系):
bash复制sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt update
sudo apt install python3.12 python3.12-venv python3.12-dev
用 python3.12 命令单独调用它,系统自带的 /usr/bin/python3 完全不受影响。
5.3 安装 JDK:openjdk 还是 Oracle JDK
安装 JDK 同理,优先用包管理器:
bash复制# 查看可用的 OpenJDK 版本
apt search openjdk
# 安装 JDK 17,这是目前最常见的选择
sudo apt install openjdk-17-jdk
安装完成后,如果你需要切换到其他 Java 版本,可以用 update-alternatives:
bash复制sudo update-alternatives --config java
它会让你从已安装的多个 Java 版本里选择默认使用的那个,不用手动改 JAVA_HOME。如果项目强制要求某个厂商的 JDK,再去官网下载 tar.gz 包,放到 /opt/jdk-xxx 目录,然后在 /etc/profile.d/java.sh 里写环境变量:
bash复制export JAVA_HOME=/opt/jdk-17
export PATH=$JAVA_HOME/bin:$PATH
这样做的目的是把环境变量配置独立出来,比直接改 /etc/profile 更清晰,也不会在系统升级时被覆盖。
5.4 Git 离线安装的备用方案
热搜词里有"git linux离线安装"。这通常发生在内网环境或者网络受限的机器上。包管理器这时候确实没法直接用,因为连软件源都访问不了。推荐提前在另一台能上网的同系统架构机器上下载好离线包,然后拷贝过去安装:
bash复制# 在能联网的 Ubuntu 机器上
sudo apt download git
# 或者
sudo apt-get install --download-only git
# 下载的 deb 包会放在 /var/cache/apt/archives/
把 git_*.deb 拷到离线机器上,然后执行:
bash复制sudo dpkg -i git_*.deb
如果提示依赖缺失,说明还要下载对应的依赖包。更省心的方案是使用 apt-offline 或在下载时用:
bash复制apt-get download $(apt-cache depends --recurse --no-recommends --no-suggests --no-conflicts --no-breaks --no-replaces --no-enhances git | grep "^[a-zA-Z0-9]" | sort -u)
这条命令会把 git 及其所有依赖的 deb 包一次性下载下来。离线安装的时候先 dpkg -i *.deb 一起装就行。对于 CentOS 系,对应方案是用 yumdownloader git --resolve。
code复制# CentOS 需要先安装 yum-utils
yum install yum-utils
yumdownloader --resolve git
5.5 安装桌面软件的思路一样
像 LibreOffice、企业微信、豆包这类带图形界面的软件,能走软件源就走软件源;软件源里没有的,优先找官方提供的 deb/rpm 包,双击或 dpkg -i 安装;最后才考虑 AppImage 这类免安装格式。优先用 deb/rpm 的原因是依赖关系能被包管理器自动补全,用 AppImage 虽然方便但每次要加执行权限,而且在某些 Wayland 环境下沙箱兼容性偶尔会有小问题。
6. 最后分享一个受益很久的习惯:把排查过程记下来
技巧类的内容写到最后,我最想回到一个朴素的建议:遇到一次问题不要只满足于解决它,花五分钟把现象、排查命令、根因、解决方案存成一个 Markdown 文件。我自己的习惯是放在 ~/notes/linux-troubleshooting/ 下,按日期命名。这个方法帮我省下了大量重复排查的时间。比如 WSL 磁盘不释放这个问题,半年前记过一遍,这周同事问我的时候,我直接把当时的命令和注意事项发过去,十分钟就帮他把 40 多 GB 空间找回来了。真正让一个人从"会用 Linux"变成"玩得转 Linux"的,往往不是拍脑袋想出来的灵感,而是这些一次一次踩坑之后沉淀下来的笔记。
