
上次那篇基础指令我把ls、cd、cat、mkdir这些入门操作过了一遍留言区不少人说“终于敢敲第一条命令了”。也有朋友追着问复制文件为什么比 Windows 慢那么多关掉终端程序为什么就断了删东西手抖怎么办软件到底怎么装才干净这些问题其实都藏在 Linux 基础指令的“续”里。这篇我接着往下写把日常用得最多的文件操作、后台任务、系统状态、用户权限、软件安装和环境变量串起来。目标很明确让你在真实服务器上干活的时候不至于因为一条rm、一次软件安装或一个环境变量把自己的环境搞崩。每个命令我都会解释它背后的原理以及我实际用过的坑建议你跟着敲一遍再自己总结一遍。1. 文件操作进阶复制、移动与删除的细节1.1 cp 和 mv复制与移动的底层差异很多刚接触 Linux 的朋友会问我cp和mv不就是一个复制一个移动嘛有什么好研究的其实这两个命令的底层逻辑差别挺大。不懂差别你会经常遇到“明明 mv 很快cp 却很慢”的困惑甚至因为搞混文件系统的边界把一个 200G 的数据库目录 “移动” 到另一台机器的挂载盘上结果等了俩小时。cp是真正的数据复制。它要读取源文件的内容再写入目标路径数据量越大耗时越长这是物理意义上的拷贝。mv在同一个文件系统内做的事只是修改目录项而已。你可以把目录想象成一张索引表文件名只是表里的一个条目mv 就是把这个条目从旧位置改到新位置数据本身纹丝不动所以几乎瞬间完成。一旦跨文件系统比如从/根分区移到/home这个独立分区或者从本地盘移到 U 盘挂载目录mv就没办法只改索引了它会退化成“复制 删除”速度自然和cp一样慢。用书架打比方cp是把一本书的内容誊抄到另一本书上抄多久取决于书有多厚。mv是在同一个书架上把书从 A 格挪到 B 格挪过去就完事。但要搬到另一个房间的书架跨文件系统就得先把书抱过去再回来清空原位这就是“复制删除”。实操上我常用的场景是# 复制整个目录必须加 -r 递归 cp -r 项目目录 /backup/项目目录_20250101 # 保留权限、属主、时间戳的归档式复制适合备份 cp -a 项目目录 /backup/项目目录_20250101 # 同一文件系统内重命名瞬间完成 mv oldname.txt newname.txt # 跨盘移动大目录这种写法会先复制再删时间不会短 mv /root/data /data2/需要注意mv和cp在目标文件已存在时默认都会直接覆盖且不提示。覆盖是瞬间发生的等你反应过来经常已经晚了。所以我现在有个习惯涉及覆盖操作的命令一定带上-i参数让它先问一句cp -i a.txt /tmp/ mv -i a.txt /tmp/1.2 rm 的杀伤力与安全删除方案rm -rf大概是 Linux 圈子里流传最广的“慎用命令”了。它的语法不复杂rm删除文件rm -r递归删除目录rm -f强制删除不提示rm -i逐个询问。组合起来rm -rf 目录就是“强制递归删除整个目录树”。网上段子里的rm -rf /能把整个系统删到只剩内核在裸奔这种命令我反而不担心你会敲错真正危险的是下面这种cd /opt/app rm -rf * # 你以为只删当前目录的东西这条命令的问题在于如果你因为路径判断失误实际所在目录不是你以为的那个*会展开成当前目录下的所有文件包括隐藏文件以外的一切内容。更隐蔽的坑是如果/opt/app下有一个子目录软链接指向别处rm -rf *会顺着链接删除目标目录里的内容这在 shell 展开时不会给你任何警告。还有一个反直觉的事实Linux 里用rm删除的文件很难恢复。ext4 文件系统删除文件时只是释放数据块的引用真正的数据并没有立刻清零但如果磁盘持续有写入或者系统开启了 TRIM那些数据块很可能立刻被标记为可复用。等你发现误删再关机、挂载只读、用工具扫描成功率已经拼运气了。对比一下Windows 有回收站Linux 的命令行rm没有回收站所以安全设计只能靠习惯。我现在的三条铁律分享给你删除之前先ls看清楚再执行rm别直接rm -rf。rm命令里不出现“通配符 关键路径”的组合比如rm -rf /opt/*这种写法直接禁止。能不用-f就不用让系统多问你一次你就有机会反悔。更讲究一点的做法是在机器上装trash-cli把删除变成“扔进回收站”trash file.txt # 扔进回收站 trash-list # 查看回收站 trash-restore # 交互式恢复或者给rm设置别名让手滑的成本低一点alias rmrm -i不过要注意这个别名会影响你自己写的脚本如果脚本里有非交互式的rm -f它会把-f追加进去覆盖-i所以不会完全失效只是多一层防护。1.3 通配符与批量操作通配符是 shell 的独门武器ls、cp、rm、mv这些命令本身并不认识*.log这种写法是 shell 先把它展开成具体文件名列表再交给命令去处理。理解这一点很重要否则你无法解释为什么rm *可以一次删掉几百个文件。基础通配符就三个*匹配任意长度任意字符包括空字符串。?匹配单个字符。[abc]匹配方括号内任意一个字符[0-9]匹配数字。举几个实际例子# 查看当前目录下所有 .log 文件 ls *.log # 复制名字是四个字符、扩展名是 .txt 的文件 cp ???? .txt /tmp/ # 写错了应该是 ????.txt # 把 file1、file2、file3 全部移到 /tmp mv file[123] /tmp/批量操作最有价值的技巧是执行破坏性命令前先用echo或者ls看一眼展开结果echo rm -rf *.tmp ls *.tmp # 确认列表里全是你要删的文件再执行 rm -rf *.tmp这个习惯帮我挡住过很多次误删。别嫌麻烦多敲一条命令的成本比恢复文件低太多了。2. 后台运行与任务管理让程序不随终端退出而退出2.1 为什么关掉终端程序就断了所有刚接触服务器的人都会遇到同一个灵魂拷问我在终端里跑了一个程序关掉终端窗口或者断网之后程序也跟着死了连日志都没留下。这在本地电脑上不太明显但在服务器上是致命问题部署的任务动不动就跑几小时。原因要追溯到几十年前的电话拨号时代。早期 Unix 系统中用户通过串行终端登录电话挂断时系统不能留着没人管的孤儿进程否则会浪费资源又无法交互。所以内核设计了一个机制终端断开时系统会向这个终端关联的前台进程组发送一个SIGHUP信号。SIGHUP的默认动作就是终止进程。这也是为什么很多配置改了之后要kill -HUP 进程号来重载配置因为 HUP 的本意是“挂断”。明白了这层机制后续的解决方案就好理解了。所谓“让程序不随界面退出而退出”本质就是做两件事要么让进程忽略SIGHUP信号要么让进程彻底脱离这个终端成为不依赖终端的独立会话。2.2 nohup 组合拳的正确用法最常见的保活方案就是nohup加上。nohup的作用是让进程忽略SIGHUP是把命令放到后台立即返回提示符。组合起来一条标准写法是nohup python app.py app.log 21 这条命令别急着背拆开看每个部分nohup python app.py用nohup启动 Python 脚本让它忽略挂断信号。 app.log把标准输出重定向到app.log文件。21把错误输出文件描述符 2也重定向到标准输出当前指向的地方文件描述符 1也就是app.log。如果这半句不写程序报错信息会直接丢到终端甚至无处可去你就只能看到进程活着却不知道它在报错。最后的把整条命令放到后台执行。有个顺序问题特别多人踩21必须写在 app.log后面。因为重定向是从左到右处理的如果写成nohup python app.py 21 app.logshell 先把21指向当前的标准输出还是终端然后再把标准输出重定向到文件结果错误输出仍然留在终端等于这半句白写了。还有一个容易忽略的点如果不指定重定向nohup会把输出写到当前目录的nohup.out文件里。这个文件只增不减跑到最后会变成几十个 G把磁盘塞满。我见过一个同事的 node 服务跑了一个月磁盘报警查下来全是nohup.out撑的。所以我现在的固定习惯是任何nohup命令都显式重定向最好加上日期nohup python app.py logs/app_$(date %Y%m%d).log 21 查看后台任务用jobs -l把后台任务调到前台用fg挂起当前进程用CtrlZ再bg让它后台继续跑。这些操作配合起来日常任务管理就够用了。2.3 screen、tmux 与 setsid更多保活姿势nohup的局限在于它只处理了SIGHUP对政治性的“进程被杀”无能为力而且你没法再回到那个进程的交互界面。如果你需要跑一个交互式程序比如夜间执行某个需要定期查看输出的脚本更合适的工具是tmux或screen。tmux的核心概念是会话session、窗口window和窗格pane。你在一个会话里开着服务通过CtrlB然后按d分离会话终端可以随便关下次需要看执行进度重新连上服务器执行tmux attach -t mysession就能回到原来的界面程序还在运行输出还在。这种感觉很像把电脑锁屏后回来继续写文档状态完全没丢。screen的用法类似screen -S myscreen # 创建一个名为 myscreen 的会话 # 在会话里跑你的任务 CtrlA D # 分离会话 screen -r myscreen # 重新接回会话setsid是另一个思路它让进程成为一个新会话的首进程完全脱离控制终端。即使你开它的终端退出进程也不会收到SIGHUP。不过它没有重连能力跑完就只能看日志所以我现在主要用tmux处理需要“人肉盯”的任务用nohup 处理那些只需要安静跑完的任务。如果这是生产环境长期运行的服务我更推荐直接用 systemd 写一个 service 单元文件定义好启动命令、重启策略、日志路径交给系统托管。nohup适合临时救急不适合长期依赖因为进程挂了没人拉起来你的服务就静悄悄没了。我的经验是临时任务用nohup 长期服务用tmux调试、systemd托管。3. 系统状态与日志从 date 到 journalctl3.1 系统时间查看与同步服务器上时间不对是个容易被忽略、一旦爆发就极其致命的问题。日志时间对不上、证书校验失败、数据库主从同步超时、计划任务乱跑追根溯源常常都是系统时钟漂了。查看系统时间最直接的是datedate # 输出示例2025年 01月 05日 星期日 14:23:45 CSTtimedatectl能看更多信息包括当前时区、是否启用了时间同步timedatectl如果你发现时区不对比如显示 UTC 而不是北京时间执行timedatectl set-timezone Asia/Shanghai这一步是永久生效的修改后不需要重启。如果你发现系统时间差了很多先确认是不是时区问题再确认 NTP 同步是否开启timedatectl set-ntp true开启后systemd-timesyncd 会自动从配置的时间服务器同步。如果服务器环境比较特殊连不上默认时间源可以手动指定# 编辑 /etc/systemd/timesyncd.conf [Time] NTPntp.aliyun.com FallbackNTPntp1.aliyun.com ntp2.aliyun.com改完重启服务systemctl restart systemd-timesyncd。老系统没有 systemd-timesyncd一般用ntpdate配合 crontab 定时同步。注意ntpdate一次性同步后为了平滑可以加-b参数直接跳变。实际上对于生产服务器我建议只在初始安装时手动同步一次之后都交给 NTP/chrony 平滑微调避免突然跳变导致运行中的应用逻辑错乱。3.2 日志文件的基本功排查问题第一站永远是日志而不是重启服务。Linux 的日志主要存在/var/log/目录下常见的几个文件要认识/var/log/syslog或/var/log/messages系统整体运行日志包括内核、服务、计划任务等。Ubuntu 用 syslogCentOS 用 messages但两者内容类似。/var/log/auth.log或/var/log/secure认证日志用户登录、sudo 提权都会记录在这里。安全检查时必看。/var/log/kern.log内核日志硬件驱动出问题往往在这里。还有/var/log/cron记录计划任务/var/log/nginx/access.log记录 Web 访问。查看日志的基本命令还是tail但它有几个变体很实用tail -f /var/log/syslog # 实时跟踪日志不断刷出新内容 tail -n 50 /var/log/syslog # 看最后 50 行 grep error /var/log/syslog # 过滤关键字-f是 follow 的意思非常适合挂在终端里观察服务启动过程。如果你用less /var/log/syslog翻日志记得按ShiftG跳到文件末尾再按F进入实时跟踪模式效果和tail -f一样。3.3 让日志成为排查故障的第一站现在很多服务由 systemd 托管日志统一走journald查日志用journalctl更顺手。# 查看某个服务的日志 journalctl -u nginx.service # 只看最近 100 行不分页直接输出 journalctl -u nginx.service -n 100 --no-pager # 实时跟踪服务日志 journalctl -u nginx.service -f我排查一个服务起不来的标准流程是这样的先systemctl status 服务名看错误信息再journalctl -u 服务名 -n 50 --no-pager看最近日志如果日志不够详细再去/var/log/下翻那个服务的独立日志。比如 Nginx 502 报错我第一眼看/var/log/nginx/error.log然后看后端服务的日志很少需要怀疑到系统层。还有一个实用习惯自己写的脚本一定要留日志。命令很简单在脚本开头加一行记录时间echo [$(date %Y-%m-%d %H:%M:%S)] 脚本开始执行 /var/log/my_script.log这样出了问题你能知道脚本到底跑没跑、跑到哪一步停了。日志轮转也很重要/etc/logrotate.d/下配好规则日志文件超过一定大小自动压缩分割避免磁盘被日志撑爆。我见过不少线上事故都是日志文件把磁盘怼满然后服务全部异常根因排查半天才发现是一行日志害的。4. 用户、组与权限Linux 的多用户设计4.1 创建用户与用户组useradd 解析Linux 从一开始就是多用户系统所有操作都是“某个人在某个权限下做的”。新建用户是运维日常中最频繁的操作之一但很多新手只记住了useradd四个字母却不知道它默认不创建家目录导致用户登录后进不去家目录处处碰壁。正确的做法是useradd -m -s /bin/bash 用户名-m表示同时创建家目录/home/用户名-s指定登录 shell一般都用/bin/bash。创建之后第一时间设置密码passwd 用户名如果用的是 Ubuntu 系统很多人会习惯用adduser注意这里的adduser是useradd的交互式封装会一步步问你密码、姓名等信息更适合新手。CentOS 的useradd则是低层命令行为更直接两者不要混着理解。用户创建完为了能执行管理任务需要加入管理员组。Ubuntu 上是sudo组CentOS/RHEL 上是wheel组# Ubuntu usermod -aG sudo 用户名 # CentOS usermod -aG wheel 用户名注意-aG而不是-G-G会覆盖用户原有的附加组没有-a追加可能把用户踢出其他必需的组。查用户信息用idid 用户名 # 输出示例uid1001(zhangsan) gid1001(zhangsan) groups1001(zhangsan),27(sudo)/etc/passwd文件里每一行代表一个用户格式是固定的用户名:x:UID:GID:描述信息:家目录:登录shell字段中间的x不代表密码真正的密码加密后存在/etc/shadow文件里普通用户无权访问所以叫“影子文件”。4.2 权限位与 chmodrwx 数字的秘密Linux 文件权限是新手最容易记混的部分但它的设计其实非常简单干净。每个文件或者目录都有三个身份属主u、属组g、其他人o每种身份有三个权限读r、写w、执行x。用ls -l查看-rw-r--r-- 1 root root 1234 Jan 10 10:00 file.txt第一个字符-表示普通文件d表示目录l表示软链接。后面九个字符每三个一组依次是属主、属组、其他人的权限。上面-rw-r--r--表示属主可读可写属组和其他人只读。数字表示法更常用。记住一个口诀r4w2x1然后相加。rwx是 4217r-x是 4015r--是 4。所以chmod 755表示属主有全部权限属组和其他人有读和执行权限。这是可执行文件的经典配置。权限数字说明r--4可读-w-2可写--x1可执行r-x5可读可执行rw-6可读可写rwx7可读可写可执行注意目录的执行权限含义和文件不一样。文件的x是能不能运行目录的x是能不能进去也就是能不能通过这个目录访问子路径。如果目录只有r权限而没有x你虽然能列出目录里的文件名但不能进入目录访问里面的文件内容。这一点排查权限问题时经常遇到。修改权限chmod 755 script.sh # 数字方式 chmod ux script.sh # 给属主加执行权限 chown root:root file.txt # 改属主和属组 chgrp dev file.txt # 只改属组umask控制新建文件的默认权限。它本身不是权限而是一个掩码。系统默认文件权限是 666目录是 777减去 umask 得到实际权限。常见的 umask 是 022所以新建文件是 644666-022新建目录是 755777-022。你可以执行umask查看当前值如果你希望别人完全看不见你的文件可以把 umask 改成 077。4.3 sudo 的边界为什么要少用 root很多人弄到服务器第一反应是切换 root 用户然后把所有操作都放在 root 下执行。这种做法的最大问题是操作全部不可追溯。一旦出了事你不知道是哪个命令、哪个用户、什么时候干的只能靠猜。日常操作应该用普通用户登录需要提权时用sudo在命令前临时借用 root 权限。sudo的核心价值不是“方便”而是“审计”每次提权都会被记录到认证日志里谁用了 sudo 干了什么一清二楚。sudo 权限配置在/etc/sudoers文件里必须用visudo命令编辑不要直接手动改文件否则语法错误可能导致所有用户都无法使用 sudo。常用的配置实例# 允许 sudo 组的用户执行所有命令需要输入密码 %sudo ALL(ALL:ALL) ALL # 允许某个用户执行 systemctl 管理服务且不需要密码 zhangsan ALL(ALL) NOPASSWD: /usr/bin/systemctlsudo -i是切换到 root 的登录 shell相当于重新以 root 身份登录会加载 root 用户的环境变量。su - root则是直接切换用户。区别在于sudo -i是基于当前用户提权日志会记录su -需要知道 root 的密码适合多管理员共管但不想暴露 root 密码的场景。我自己使用 sudo 的原则是能写进 sudoers 的白名单命令就不给 ALL 权限能用普通用户跑的服务绝不给 root需要长期保持 root 身份的场合一定用sudo -i而不是裸su -这样审计信息不会丢。服务器上所有自动化脚本也尽量用专用账号运行而不是 root 一跑到底。5. 软件安装与环境变量从 apt 到编译安装5.1 包管理器apt、yum 到底有什么区别Linux 软件安装和 Windows 最大的不同是它有一套完善的包管理系统。Debian 系Ubuntu、Debian用aptRed Hat 系CentOS、RHEL、Fedora用yum或dnfArch 系用pacman。它们的底层机制不同但使用思维一致从软件源下载预编译的二进制包自动解决依赖关系。apt 的基本用法apt update # 更新软件源缓存列表 apt install nginx # 安装软件 apt upgrade # 升级所有可升级的软件 apt remove nginx # 移除软件 apt --fix-broken install # 修复依赖损坏yum/dnf 的基本用法类似yum install nginx yum update yum remove nginx一定要理解apt update为什么是第一步。它并不是升级软件而是从软件源服务器拉取最新的软件包列表到本地缓存。你本地的列表是上次 update 时的快照如果不 updateinstall 时可能找不到刚发布的软件版本或者依赖关系对不上。所以任何 apt 操作的第一步永远是apt update。软件源配置文件在/etc/apt/sources.list或/etc/yum.repos.d/*.repo。如果你在国内服务器上安装软件速度很慢可以把软件源切换到国内高校或云厂商提供的公共开源镜像站具体配置方法通常在对应镜像站的帮助页面里有说明。但要注意换源只换大厂或高校的稳定镜像不要随便添加来历不明的第三方源否则轻则依赖冲突重则整个系统的包管理混乱。我踩过最大的坑是 Ubuntu 上乱加 PPA某个包版本被强行拉高其他应用依赖全部崩溃最后只能重装系统还原软件列表。5.2 源码编译安装gcc 实战不是所有软件都能在官方源里找到。有些场景下你需要从源码编译比如官方源版本太旧、需要自定义编译参数、或者软件本身就是源码包。理解编译安装的基本套路比背一堆命令更有价值。标准流程就是很多人都听说过的“四步走”./configure --prefix/opt/myapp make -j$(nproc) make install第一步./configure会检测系统环境确认依赖库是否齐全并生成 Makefile。--prefix/opt/myapp指定安装目录这是最关键的一个参数。如果不指定很多软件默认装到/usr/local卸载时没有统一目录残渣很难清干净。指定到独立目录后比如/opt/myapp删掉这个目录就等于卸载了非常干净。第二步make是编译-j$(nproc)表示用 CPU 的全部核心并行编译能明显缩短时间。遇到大项目比如编译内核或者 Qt这个参数能省一半以上的时间。第三步make install是把编译好的文件安装到--prefix指定的目录。gcc 是 Linux 下最基础的编译器这里用它演示一个最小编译实操# 创建 hello.c vim hello.c源码内容#include stdio.h int main() { printf(Hello, Linux!\n); return 0; }编译并执行gcc hello.c -o hello ./hello # 输出Hello, Linux!-o指定输出文件名。日常开发我至少会加两个参数gcc hello.c -o hello -Wall -Wextra -g-Wall和-Wextra显示所有警告-g生成调试信息配合 gdb 排错必备。编译新手最容易遇到的是 “fatal error: stdio.h: No such file or directory”说明编译环境缺少基础头文件。Ubuntu 装build-essentialCentOS 装Development Tools# Ubuntu apt install build-essential # CentOS yum groupinstall Development Tools装完再编译就正常了。编译安装的程序如果出现动态库找不到报错类似 “error while loading shared libraries”需要执行ldconfig让它刷新链接库缓存或者把库目录写进/etc/ld.so.conf.d/下的配置文件。这个问题常见于自己编译安装的软件系统默认不会搜到/usr/local/lib之类的地方需要手动告诉加载器。5.3 PATH 与环境变量为什么命令能找到你敲python能运行 Python敲gcc能编译靠的是PATH环境变量。可以把它理解成一张“命令搜索地图”shell 收到你输入的命令名后会按 PATH 里列出的目录顺序逐个查找同名可执行文件找到就运行都找不到就报command not found。查看当前的 PATHecho $PATH # 输出示例/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin如果你想让自己编译安装的程序能被直接启动就把它的目录加进 PATH。临时生效export PATH/opt/myapp/bin:$PATH注意变量赋值里用了$PATH这是把原有路径保留下来在后面追加新路径。如果写成PATH/opt/myapp/bin等于清空了所有原有路径紧接着连ls、vi都找不到了当前 shell 直接瘫痪。我见过有人手滑这样写过一次当场所有命令失效只能拆了重登这是新手最容易踩的环境变量坑。永久生效需要写入 shell 配置文件。交互式登录 shell 会读取~/.bashrcbash 环境在文件末尾加一行export PATH/opt/myapp/bin:$PATH然后生效source ~/.bashrc关于 Anaconda 的环境变量也值得一提。安装 Anaconda 时安装程序会问你是否初始化 conda这一步实际上就是把 conda 的初始化代码写进~/.bashrc涉及PATH的调整。如果你之前已经手工设置了PATH要小心和 conda 初始化互相覆盖顺序不对可能导致python指向的不是 Anaconda 自带的环境。遇到这种问题最直接的排查方法是执行which python看它到底指向哪里然后检查~/.bashrc里 PATH 相关的行有没有被重复写入。环境变量是子进程继承的你在当前 shellexport了某个变量在这个 shell 里启动的程序都能看到它但如果你另开一个终端或者用cron跑脚本那些环境是全新的不会自动继承你手工设的值。所以涉及计划任务、服务启动需要把环境变量写在/etc/profile或服务单元文件里而不是指望 shell 里的export能传递。这也是很多脚本在终端里跑没问题、放到 crontab 里就报“command not found”的原因。6. 高频故障排查小抄6.1 command not found 的三类原因“command not found”是 Linux 新手最常撞见的错误但很多人第一反应都是“我是不是把系统搞坏了”。其实它只有三类原因第一软件根本没装。处理方式是用包管理器装上或者确认软件名是否正确。比如你敲nginx -v报 not found先看是不是没装 Nginx。第二软件装了但目录不在 PATH 里。这类最隐蔽。很多服务程序安装后默认放在/usr/sbin/而普通用户的 PATH 可能不包含这个目录。用绝对路径执行一下就知道/usr/sbin/nginx -v第三文件格式/架构不对。比如你从网上下了一个二进制工具直接执行报 “cannot execute binary file”通常是架构不匹配x86 的包安到 ARM 机器上或者缺少动态库。排查顺序建议是which 命令名 # 看看系统有没有找到它 dpkg -S 文件名 # Ubuntu查某个文件属于哪个包 rpm -qf 文件名 # CentOS查文件属于哪个包有些时候软件刚装完当前 shell 的哈希表还记着旧信息执行hash -r清一下缓存就好了。别忘了重新登录一次终端让/etc/profile重新加载。6.2 删不掉的文件夹与文件删不掉的文件背后永远是权限、属性、占用这三件事之一。权限最简单报Permission denied就说明你这个用户没权限。先ls -l看属主确认这个文件是不是该你管的再用sudo提权删除不要一上来就chmod 777把整个目录放开这个习惯能避免很多事故。属性问题比较隐蔽。某些文件被设置了不可修改属性immutable就算你是 root 也删不掉这是安全加固时常用的手段。检查方法lsattr file.txt如果输出里有i就是不可修改属性移除后删除chattr -i file.txt rm file.txt占用问题在日志和临时文件上最常见。一个文件被进程打开比如 Nginx 写日志删它时提示 “Text file busy” 或者 “Operation not permitted”但文件确实已经看不到了。这时空间也没释放问题出在你“删了文件”但“可写句柄还在”。用lsof查谁占着它lsof | grep deleted找到进程 PID确认后重启进程或 kill 掉空间就释放了。这个场景排错时特别经典df -h显示磁盘满但你du -sh *翻遍目录也找不到大文件就是因为大文件已经被 delete 了只是进程没关。6.3 磁盘空间莫名满掉排查磁盘满的基本流程是固定的df -h # 看哪个挂载点满了 du -sh /var/log/* # 从可能增量的目录开始排查 du -sh /* 2/dev/null # 从根目录逐层找大目录du -sh *能看到当前目录下每个子目录和文件占用的空间大小。排查时从根开始一个个du找到一个异常膨胀的目录再进去继续du层层定位。很多次问题都出在/var/log、/tmp、以及应用日志目录。df -i也要看一眼它显示 inode 使用率。如果一个挂载点上文件数量过多即使磁盘空间没满也可能报 “No space left on device”因为 inode 用完了没法再创建新文件。这种问题常见于小文件成千上万的目录比如邮件队列或者临时文件目录。服务日志不断膨胀是最常见的隐性杀手。如果服务进程一直持有删除后未释放的日志句柄解决方法是重启服务如果日志文件正常在增长就要配置 logrotate 让它按大小或按天轮转压缩。线上环境我的经验是给所有应用日志设 100M 轮转线保留 7 份磁盘永远不会被日志吃掉。6.4 乱码文件名处理文件名以-开头怎么办这是 Linux 下著名的“杠精问题”。执行rm -abc.txtrm 会把-abc.txt当成参数解析报错而不是执行。解决办法是给命令加--它表示“后面所有内容都当成文件名不是选项”rm -- -abc.txt mv -- -abc.txt newname.txt文件名包含空格或者特殊字符时最稳妥的方式不是手打全名而是按Tab自动补全shell 会自动把特殊字符转义好。查看时用ls -b它会把不可见字符显示成转义序列比直接看终端舒服。如果文件名真的是乱码连可打印字符都没有可以按 inode 编号操作# 先找到 inode ls -li # 第一列就是 inode 编号 # 按 inode 删除 find . -inum 1234567 -exec rm {} \;这套方法在文件系统异常恢复时也能用比瞪着眼睛猜文件名靠谱得多。最后分享一个我自己的操作习惯在服务器上执行任何可能破坏性的命令之前先停三秒把命令用echo或ls展开看一遍再动手。基础指令从来不只是一串串命令的背诵更关键的是建立一套对系统运作机制的直觉——知道mv为什么快、知道删除为什么难恢复、知道环境变量为什么有时不生效。有了这些直觉你再遇到任何 Linux 问题至少能判断问题出在哪个环节而不是像无头苍蝇一样重启解决一切。