资讯动态

Linux进阶实战:文件系统、权限与服务排错技巧解析

发布时间:2026/10/21 12:12:17 来源:尧图企业网站定制
Linux系列更新到第八篇了标题里的Liunx是手误其实这期要聊的还是Linux。系列能走到这说实话我自己也挺有成就感从最基础的cd、ls、cp开始一步步到文件操作、权限、服务和脚本中间踩过的坑基本都写进前面几期了。到了第八期我不想再给你堆一堆用不上的冷门参数而是把平时真正高频、真正容易出事的点再往深挖一层。如果你已经会基本的命令操作却总觉得命令是命令、系统是系统遇到磁盘满了、权限报错、服务起不来这类问题还是不知道从哪下手那这期就是写给你的。这期会从文件系统底层认识讲起再到权限、磁盘排查、服务排错最后聊几个小脚本技巧内容偏实战适合已经入门、想真正提升动手能力的学习者。1. 文件系统的底层认识inode与挂载点的关系1.1 认识“一切皆文件”的真实含义刚学Linux的时候所有人都告诉你“一切皆文件”但真正用起来这句话很容易让人误解。文件不只是你看到的txt、jpg目录是文件设备是文件甚至进程的输入输出也对应着文件描述符。你执行ls、cd、mv本质都是在和文件系统打交道。但这里有个关键点你看到的文件路径并不等于文件本身。路径只是从根目录到目标文件的一条“导航路线”真正定位文件的是inode号。我最早没搞懂这点结果差点把服务器数据弄丢。当时别人让我清理日志我习惯性地进了/var/log看了一堆不看不懂的文件顺手执行rm -rf删了一个名字看起来像临时文件的目录结果业务接口立刻报错。后来才知道那个目录是另一个服务挂载点真正的数据在另一块磁盘上路径只是个入口。理解挂载点最简单的方式就是把它想象成一个“门牌号”。你家有很多房间每个房间有一扇门门牌号就是路径房间里的家具才是真实数据。你在门口换了一块新地板实际上换的是整个房间的地板。mount命令干的就是把一块磁盘和某个目录关联起来的事umount就是把门拆掉。用df -h看的是每个文件系统的使用情况用ls -l看的是目录内容这两者不能混为一谈。1.2 inode与硬链接、软链接的实战inode这个概念必须摸着石头过河才能真正理解。每个文件都有一个inode里面存着文件大小、权限、属主、时间戳和指向数据块的指针但不存文件名。文件名和inode的对应关系存放在它所在的目录里。所以所谓创建硬链接就是在另一个目录里增加一个“文件名到同一inode”的记录。很多人问我软链接和硬链接到底选哪个我给你的答案很直接日常使用软件链接够用了硬链接要谨慎。软链接是一个独立文件内容存的是目标路径目标被删除就失效硬链接不产生新inode文件计数加1只要还有硬链接存在数据就还在。实验一下就清楚了echo hello world /tmp/test_original ln /tmp/test_original /tmp/test_hard ln -s /tmp/test_original /tmp/test_soft ls -li /tmp/test_original /tmp/test_hard /tmp/test_soft你会看到硬链接的inode和原文件一样软链接的inode不同而且软链接后面会多一个箭头。这时候删除原文件硬链接还能读出内容软链接就变成红色断链了。目录是不能随便做硬链接的系统出于防止环路考虑默认不允许普通用户对目录建硬链接这是设计上的安全约束。实战中inode还有个很重要的用途就是配合find -inum找文件。比如目录里出现了乱码文件名你输入名字都难但可以用ls -i看到inode号再用find /xxx -inum inode号 -delete删掉。我自己遇到过一次磁盘inode耗尽的问题就是靠这个思路处理的后面第四章细说。2. 文件操作中的高频细节别让简单命令坑了你2.1 通配符、引号与特殊字符的边界Linux命令里最容易阴沟翻船的地方就是通配符和引号。先说通配符shell在展开路径时才会处理*、?、[]它们和你搜文本用的正则不是一回事。很多人以为rm *.txt里的*是正则其实它只是匹配当前目录下所有以.txt结尾的文件名。如果你想匹配文件名里带点号的文件通配符的.没有特殊意义不需要转义。引号问题更隐蔽。单引号内所有字符都按字面处理双引号会保留$、反引号和\的特殊含义。例子nameAlice echo $name # 输出 $name echo $name # 输出 Alice这个区别到了写脚本、传参数的时候极为关键。有一次某同学写了个脚本把路径用单引号包起来结果变量没被展开程序一直说找不到目录排查半天才发现是引号用错了。还有一个高频坑是文件名里有空格比如my file.txt直接把名字传给命令会被当成两个参数解决办法是加引号或者用反斜杠转义空格。命令行里遇到开头的横线也要当心比如你创建一个文件叫-v直接rm -v不会删文件而是把-v当成rm的选项。正确处理方式是在前面加--表示选项结束即rm -- -v。这一点知道的人不少但真遇到时手忙脚乱的也多。2.2 批量文件操作的安全姿势批量操作文件最容易翻车的就是rm -rf加find组合。网上很多教程直接让你写find /some/path -name *.log -exec rm -rf {} \;这个写法不是说不能用而是风险极高一旦路径写错比如路径变量没赋值成功就会变成rm -rf /这种灾难现场。我现在的习惯是执行删除前先列出来看看再加一个-delete而且不会在一条命令里直接删而是先导出列表find /some/path -name *.log -print /tmp/to_delete_list.txt wc -l /tmp/to_delete_list.txt head /tmp/to_delete_list.txt确认名单没有异常之后再用xargs传参删除。重点是find输出和xargs之间有一个隐形的坑就是文件名带空格时默认会被拆成多个参数。解决办法是-print0配合xargs -0让每一项以空字符分隔这样带空格、换行的文件名都能安全处理find /some/path -name *.log -print0 | xargs -0 rm -f批量重命名也是高频操作。系统自带的rename命令在不同发行版上语法不同有的支持Perl表达式有的只支持简单替换。我建议你在执行之前先用-ndry-run看看效果确认没问题再实际执行。比如批量把.jpg改成.jpegrename s/\.jpg$/.jpeg/ *.jpg # 支持Perl语法的版本如果你不确定服务器上是哪种版本直接看手册或者干脆用shell循环自己写反而更可控。用循环的另一种思路是每个文件都经过一次mv配合basename和dirname截取路径灵活性更高。3. 用户与权限权限管理不只是chmod 7773.1 用户和组管理的基础命令用户权限管理是Linux里绕不过去的核心点但我见过太多人只会useradd加chmod 777一出问题就把整个目录权限全放开这是非常危险的习惯。先搞清楚怎么正确创建和管理用户。示例创建一个新用户并加入特定组useradd -m -s /bin/bash -G developers zhangsan passwd zhangsan这里-m表示创建home目录-s指定登录shell-G附加到developers组。如果用户建错了想改默认组或附加组用usermod -g和usermod -G。注意usermod -G在旧版本上会覆盖原有附加组需要写成usermod -aG追加少一个-a就可能把用户踢出一堆组这个坑我踩过两次。删除用户比创建用户更需要谨慎。userdel不会自动删除用户家目录加上-r才会删但一旦删了家目录备份的机会都没了。实操上我会先锁定用户而不是直接删除尤其是还在跑定时任务或服务的账号# 禁用密码登录但不删除数据结构 passwd -l username usermod -s /sbin/nologin username需要给某个用户临时root权限时优先考虑sudo而不是把用户直接加到/etc/passwd里改UID。正确做法是把用户加到sudo组或者编辑/etc/sudoers.d/下独立文件加一行zhangsan ALL(ALL) NOPASSWD:ALL直接写/etc/sudoers也可以但我坚持用独立文件因为可追溯、不会把系统默认配置改坏。3.2 权限位深入解读与常见故障排查权限位看起来就九个字符但理解不深很容易出问题。rwx对文件和对目录的含义完全不一样对文件r是能读内容w是能改内容x是能执行对目录r是能列出目录里的文件名w是能创建和删除文件x是能进入目录。许多“权限明明对了但还是访问不了”的问题问题往往出在父目录的x权限上。常见场景你给用户配置了一个子目录的r权限但父目录没有x用户根本进不去表现就是“Permission denied”。chmod的两种表达式都建议掌握。数字法直观但容易算错符号法更语义化适合临时调整chmod ux /path/file # 给属主加执行权限 chmod g-w,o-r /path/file # 组去掉写其他人去掉读 chmod 750 /path/file # 属主rwx组r-x其他无权限还有一个容易被忽略的是umask。它决定了新建文件默认权限默认文件通常是666减去umask目录是777减去umask。比如umask是022新建文件就是644新建目录就是755。如果你发现创建出来的配置文件总是带组写权限多半是umask设错了。特殊权限位SUID、SGID和Sticky bit专门写一下因为很容易引发安全事故。SUID位出现时普通用户运行这个程序进程的有效身份会变成文件属主。典型例子是/usr/bin/passwd普通用户需要修改自己的密码而密码文件只有root能写靠SUID机制让普通用户暂时以root身份去执行。如果你给一个你自己写的脚本加上SUID风险极高脚本里任何可控变量都可能被利用。SGID对目录来说会让新建文件自动继承目录的属组Sticky bit最典型的例子是/tmp在这个目录下用户只能删除属于自己文件不能删别人的。这三个权限位用chmod数字法分别是4000、2000、1000叠加使用就是6000、5000等。排查权限故障时先看id确认当前身份和组再用namei -l /path/to/file看路径上每层目录的权限。还有一个大招ACL访问控制列表。当ls -l权限位后面多了一个说明文件用了ACL不能只看传统权限了要用getfacl看用setfacl改。我记得有一个部署场景一个服务账号始终读不了另一个目录chmod按传统权限怎么改都不生效最后发现是ACL里显式拒绝了该账号这种问题不用getfacl根本查不出来。4. 磁盘空间不足用这些命令把空间找回来4.1 df与du的正确使用姿势磁盘满了是每个Linux用户迟早会遇到的经典问题。处理之前先分清两种“满”一种是空间满一种是inode满。df -h看的是前者df -i看的是后者。大多数新人只会盯df -h结果分区明明还有几十GB却老是报“No space left on device”那就是inode被小文件占满了。查空间占用的时候du比你以为的要复杂。直接在根目录跑du -sh /会非常慢因为这会把所有子目录都递归统计一遍。更实用的方法是指定分层深度du -h --max-depth1 /var 2/dev/null | sort -hr这样一眼就能看出/var/log、/var/lib哪个占了大头。在任意目录里用du -sh *也能快速找出这个目录下体积最大的文件和目录但没有排序时输出很乱我习惯加管道排序du -sh /path/* 2/dev/null | sort -hr | head -20还有一类“空间对不上”的困惑df显示使用率很高但du把所有文件加起来却对不上。这通常有三种原因一是文件被删除但还被进程占用二是某个挂载点下面藏了另一个文件系统三是隐藏的大文件你没权限看。先不要慌按下一小节的思路排查。4.2 inode耗尽、已删除文件与隐藏空间先解决那种“空间明明足够却写不进东西”的情况。df -i看一下如果IUsed达到100%说明inode耗尽了。inode耗尽常见于存放大量小文件的目录比如邮件队列、缓存目录、临时文件目录。某台机器上一次出问题就是监控程序把大量告警写入/tmp目录每个告警一行一个小文件文件数量几十万个直接把inode占满。找到大量小文件的位置后可以用find统计数量find /tmp -type f | wc -l find /tmp -type d | wc -l确认是哪些目录之后再针对性地清理。清理时同样注意别误删先看文件时间戳和大小再用-mtime限制范围比如只删除7天前的临时文件find /tmp -type f -mtime 7 -delete再处理已删除但还被进程占用的文件。这在日志服务上最常见某人用rm删了在写的大日志文件但写日志的进程一直持有这个文件句柄空间根本不会真正释放。检查办法是找占用了已删除文件的进程lsof L1或者去对应的/proc/pid/fd目录看符号链接文件后面标记为(deleted)就是已经被删除但还开着句柄。这个时候重启对应服务空间就会立刻回收。别傻乎乎地在根目录反复du -sh找文件文件已经不在目录树里了。还有一个偏冷门但实用的点ext4文件系统默认会预留5%的空间给root用户所以非root用户会看到空间满了root还能写。如果磁盘很大5%的预留也相当可观。你可以用tune2fs -m调整预留比例但我不建议设成0完全没有预留空间后一旦磁盘碎片化严重可能连root都无法完成关键维护操作。5. 服务挂了怎么排查从日志到网络的一整套思路5.1 用dmesg和journalctl定位问题服务起不来新人第一反应是看进程状态然后瞎猜。其实排错是有套路的第一步永远是看日志。现代系统上推荐优先用journalctl它会汇总各个服务的日志比翻/var/log下的散装文件高效太多。常用三板斧# 看某个服务的全部日志 journalctl -u nginx.service # 看最近一次的报错上下文带颜色还自动跳到错误位置 journalctl -xe # 查看最近20行的某个服务日志 journalctl -u nginx.service -n 20如果服务启动就失败用systemctl status nginx.service能看到最后几行错误和进程退出码但这往往不够配合journalctl才能看到具体的配置解析错误。我见过太多人抱怨“服务起不来”结果nginx -t一下配置文件语法检查都不过错误信息就在眼前。排查内核层面的问题要用dmesg。它存的是内核环形缓冲区日志会记录硬件错误、段错误、OOM等关键事件。比如某应用突然崩溃不用急着怀疑代码先看dmesg -T也许能看到Out of memory: Killed process说明是内存不足被内核杀了。dmesg默认不带时间戳加-T会显示人类可读时间这个细节建议记住。还有一个很容易漏掉的journalctl日志分优先级error级别的日志可以单独筛出来journalctl -p err -b-p err表示只看error及以上级别-b表示本次开机以来的日志。系统性崩溃后用这条命令快速扫一遍往往能直接定位到出问题的驱动或服务。5.2 网络、端口与服务的联动排查服务进程没报错但外部访问不通就要把网络链路检查起来。先确认服务监听的端口ss -lntp-l只看监听状态-n不做域名解析-t只看TCP-p显示进程信息。它和netstat功能相似但netstat逐渐淘汰了新环境很多机器没有。如果看不到预期端口说明进程根本没起来或者端口冲突换一个端口起。端口冲突的典型现象是重启服务时报“Address already in use”。先用ss -lntp | grep 端口号找到占用进程然后确认是不是你想接管的那个再决定kill还是改配置。千万别一上来就pkill误杀其他业务进程的教训太多了。进程状态正常、端口也监听依然连不上就得分段排查。本地先起一个测试连接curl -v http://127.0.0.1:8080/能看到详细握手过程卡在哪一步一目了然。如果本机能通外部不通再看防火墙firewall-cmd、ufw、iptables不同系统各有差异但排查方向一致都是看对应端口是否有DROP规则。远程测试端口还可以用nc -vz IP 端口测TCP连通性很快。最后是配置生效的问题。改完配置文件一些服务不会自动加载新配置需要systemctl reload或restart。reload是平滑重载restart是彻底重启对于nginx这类的服务改配置后建议先nginx -t语法检查再reload。如果你改了监听端口但忘了重启那外部当然是连不上的这种低级失误排查起来最花时间。6. 写点小脚本把重复劳动变成一条命令6.1 脚本的开头、变量与常见误区Linux的日常运维离不开shell脚本哪怕你只是想把几条命令串起来也值回票价。写脚本第一个习惯就是shebang它告诉系统用什么解释器执行#!/bin/bash别省这个因为不同解释器语法差异很大明明你用的是bash语法有些人用sh执行可能就报错。脚本开头我通常会加一句set -e意思是只要任一条命令返回非零状态就立即退出避免错误命令继续执行导致更大的问题。但这里有个反直觉的地方如果你某条命令预期会失败还希望脚本继续跑就要临时关闭或者对失败单独处理。变量赋值等号两边不要有空格这是新手最常犯的错。name alice会被当成三条命令必须写成namealice。引用变量时习惯性加双引号比如cp $file /backup/如果file变量里有空格不加引号就会拆成多个参数最终cp命令参数错乱。这个坑我在批量处理文件时遇到过无数次。条件判断也是一堆细节。[ $a $b ]两边必须有空格if后面必须接命令判断变量长度用-z和-n。不要用[ $a yes ]这种写法变量为空时它会变成[ yes ]语法直接报错。调试脚本时用bash -x script.sh它会逐行显示执行的命令和变量值是我用过最好的排错手段没有之一。另一个常见误区是脚本的执行方式。./script.sh需要脚本有执行权限且shebang生效bash script.sh则不需要。需要改环境变量的脚本必须用source script.sh或. script.sh执行直接跑子进程的话环境变量不会传给当前shell。6.2 定时任务与别名让系统替你干活脚本写好了不可能每次都手动执行定时任务就派上用场。crontab格式五段分、时、日、月、周每个字段后面跟要执行的命令。举例# 每天凌晨2点30分执行备份脚本 30 2 * * * /opt/scripts/backup.sh /var/log/backup.log 21这里21把标准错误也重定向到日志文件这样出错了你才能在日志里看到根源。定时任务一个高频坑是环境变量差异cron执行时的PATH极其精简很多命令找不到所以脚本里要么写命令绝对路径要么在脚本开头显式加上环境变量例如export PATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin。我建议给脚本输出加个时间戳方便排查定时任务到底跑没跑echo $(date %F %T) backup finished /var/log/backup.log还有一类让生活更轻松的办法是shell别名。alias llls -lhF这种别名师出无名但大多数人改了不记得写进配置结果换个会话就失效。要永久生效写在~/.bashrc而且alias只能在交互式shell里用脚本里请使用完整命令。新建别名后执行source ~/.bashrc让配置生效不用重新登录。举一个完整的小例子我想把/var/log下7天前的日志压缩后转移到备份目录#!/bin/bash set -e LOG_DIR/var/log BACKUP_DIR/backup/logs mkdir -p $BACKUP_DIR find $LOG_DIR -type f -name *.log -mtime 7 -exec gzip {} \; find $LOG_DIR -type f -name *.log.gz -mtime 7 -exec mv {} $BACKUP_DIR \;实际操作中我会先用echo或find -print验证匹配的文件再真正执行压缩和移动。批量操作脚本宁可慢一点也要给自己留一个确认的机会。我个人现在最常用的一条别名是把上面这套排查路径压缩成一条命令比如alias logerrjournalctl -p err -b --no-pager | tail -50效果很明显。不过这期的内容已经很满了建议你先把前面每条命令都实际敲一遍踩几个坑之后再回来写自己的清理脚本收获会更大。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑