大家好,我是程序员柏风,AI 最全的 Vibecoding 学习地图 http://coderoadmap.cn 作者。
文件与目录
这一课你将能做到
- 熟练使用
cp、mv、rm、touch管理文件,理解每个操作的隐含风险 - 区分软链接与硬链接,能在部署场景中选择合适的链接方式管理配置文件或日志目录
- 读懂
ls -l输出中的 inode 与链接数信息,排查「磁盘空间不足但找不到大文件」的诡异问题
为什么要学
文件与目录是 Linux 世界的基本原子。你在服务器上部署 Spring Boot 应用时,需要上传 JAR 包、移动配置文件、备份日志、清理过期数据;排查 MySQL 数据目录膨胀时,需要知道文件真正占了多少块、有没有被删除但仍被进程占用的「幽灵文件」。硬链接和软链接更是运维利器——用软链接把 Nginx 配置指向版本控制目录,用硬链接做日志快照,都是生产环境的常规操作。
面试中,inode 是什么、软硬链接区别、删除文件后空间不释放的原因,属于高频考点。
先建立图像
文件系统底层视角:
inode 表(元数据仓库) 数据块(实际内容)
+--------+ +------------------+
| inode | --指向--> | "Hello, world!" |
| 1234 | | |
+--------+ +------------------+
^
|
文件名(目录项)
+----------+
| myfile |
+----------+
- inode:文件的「身份证号」,存着权限、大小、时间、数据块位置等元数据,唯独不存文件名。
- 目录项:文件名和 inode 号的映射表。你看到的「文件」其实是目录里的一条记录。
- 硬链接:给同一个 inode 再起一个名字。两个名字完全平等,删一个不影响另一个。
- 软链接(符号链接):存的是「另一个路径的快捷方式」,有自己的 inode,指向目标路径字符串。
跟着做
步骤 1:创建、查看、移动、复制文件
cd ~
touch app.conf # 创建空文件,或更新已有文件的时间戳
ls -l app.conf # 查看文件信息
echo "server { listen 80; }" > app.conf # 写入内容(覆盖)
echo "location /api {}" >> app.conf # 追加内容
cp app.conf app.conf.bak # 复制文件
mv app.conf.bak /tmp/ # 移动文件到 /tmp
mv app.conf nginx.conf # 重命名文件
rm /tmp/app.conf.bak # 删除文件
步骤 2:理解 inode——查看文件「身份证号」
ls -li nginx.conf # -i 参数显示 inode 号
# 输出示例:1234567 -rw-r--r-- 1 ubuntu ubuntu 35 Oct 7 10:00 nginx.conf
# ↑ inode
stat nginx.conf # 查看完整的 inode 元数据
步骤 3:创建硬链接
ln nginx.conf nginx-hard.conf # 创建硬链接
ls -li nginx.conf nginx-hard.conf
# 两者 inode 号完全相同,链接数(第三列)从 1 变成 2
rm nginx.conf # 删除原文件
ls -li nginx-hard.conf # 硬链接仍然可以访问,inode 还在
rm nginx-hard.conf # 最后一个名字删除,inode 才被释放
步骤 4:创建软链接
ln -s /etc/nginx/nginx.conf my-nginx.conf # 创建软链接
ls -l my-nginx.conf
# 输出:lrwxrwxrwx ... my-nginx.conf -> /etc/nginx/nginx.conf
cat my-nginx.conf # 可以正常读取目标文件内容
rm /etc/nginx/nginx.conf # 如果目标被删(先备份再试)
cat my-nginx.conf # 报错:No such file or directory(悬空的软链接)
生产场景:Spring Boot 应用升级时,用软链接指向当前版本 JAR:/opt/app/app.jar -> /opt/app/app-v1.2.3.jar。升级时只需修改软链接指向新版本,回滚瞬间完成。
步骤 5:跨设备链接限制
df -h # 查看各挂载点所属设备
ln /var/log/syslog ~/syslog-hard # 如果 /var 和 /home 是不同分区,硬链接会失败
# 报错:Invalid cross-device link
ln -s /var/log/syslog ~/syslog-soft # 软链接可以跨分区
硬链接不能跨文件系统,因为不同分区的 inode 表是独立的。软链接本质是路径字符串,不受此限制。
步骤 6:查找并处理「已删除但仍占用空间」的文件
这是生产环境排查磁盘满的经典场景:
sudo lsof | grep deleted # 查找已删除但仍被进程占用的文件
# 常见 culprit:MySQL 的慢查询日志、Redis 的 AOF 重写、Spring Boot 的日志文件
# 被 logrotate 压缩或删除后,如果应用进程没重启,inode 不会被释放
sudo systemctl restart mysql # 重启服务释放空间(或优雅地让应用重载日志句柄)
步骤 7:目录操作进阶
mkdir -p /tmp/demo/{a,b,c}/logs # 花括号展开,一次性创建多组目录树
cp -r /etc/nginx /tmp/demo/ # -r 递归复制目录
mv /tmp/demo/nginx /tmp/demo/nginx2 # 重命名目录
rm -rf /tmp/demo # 强制删除目录及其内容
-r 是 recursive(递归),对目录操作几乎必加。rm -rf 是「核弹按钮」,执行前务必确认路径。
讲清楚要点
- 文件名只是目录里的一个映射:同一个 inode 可以有多个文件名(硬链接),删除文件名只是减少链接计数,计数归零才真正释放数据块。
- 软链接存的是路径字符串:目标文件移动或删除后,软链接就会「悬空」(dangling)。
ls -l时能看到箭头指向,链接权限位永远是rwxrwxrwx(权限由目标文件决定)。 - 硬链接不能跨文件系统:因为 inode 只在单个文件系统内唯一。软链接可以跨分区、跨网络(指向 NFS 路径)。
- 目录的链接数至少为 2:一个是目录自身的
.,一个是父目录里的目录名。每创建一个子目录,父目录的链接数加 1(因为子目录里有..)。 cp默认复制的是内容而非链接:复制软链接时,默认会复制目标文件的内容。想保留链接属性,需加-d或-a(archive,保留几乎所有属性)。mv在同一文件系统内是「重命名」:只是修改目录项的 inode 映射,不移动数据块,所以极快。跨文件系统才是真正的复制+删除。touch不仅能创建空文件:还能用来更新文件的访问时间(atime)和修改时间(mtime),在脚本里常用来标记任务执行时间。
你可能会懵的地方
-
为什么
ls -l的第三列数字有时是 1,有时是 2? 那是链接数(硬链接计数)。普通文件刚创建时是 1;创建了一个硬链接后变成 2;目录默认至少 2(.和自身目录项)。 -
软链接和 Windows 快捷方式一样吗? 很像,但 Linux 软链接对应用程序透明。程序读取软链接时,内核自动跟随到目标文件;Windows 快捷方式只是一个
.lnk文件,程序需要显式解析。 -
删除文件后
df -h显示空间没释放? 因为还有进程持有该文件的文件描述符。用lsof +L1或lsof | grep deleted找到占用进程,重启或通知进程重载配置。 -
cp -a和cp -r有什么区别?-a是 archive 模式,等于-dR --preserve=all,会保留软链接、硬链接、权限、时间戳。复制备份时推荐-a。 -
为什么我不能在别人的主目录里创建文件? Linux 严格的权限隔离:每个用户的主目录默认权限是
drwxr-xr-x(755),其他人只有读和执行(进入)权限,没有写权限。
错法对照
| 错法 | 问题 | 正解 |
|---|---|---|
rm -rf / tmp/logs 多空格 | 解析为删除根目录和 tmp/logs | rm -rf /tmp/logs |
cp nginx.conf /etc/nginx/ 直接覆盖系统配置 | 没有备份,无法回滚 | cp nginx.conf /etc/nginx/nginx.conf.bak.$(date +%F) 再复制 |
ln file1 file2 想建软链接 | 默认创建的是硬链接 | 软链接必须加 -s:ln -s file1 file2 |
cp -r link_dir target_dir 复制包含软链接的目录 | 软链接指向的目标被重复复制,浪费空间 | cp -a source_dir target_dir |
mv /var/log/big.log /tmp 跨分区移动大文件 | 实际是复制+删除,I/O 极高且耗时 | 在同一分区用 mv;跨分区建议先 gzip 再传输 |
rm bigfile 后磁盘空间没变化 | 进程仍占用句柄 | `lsof |
卡点急救
-
现象:
ln: failed to create hard link: Invalid cross-device link硬链接不能跨分区。解决:改用软链接ln -s;或者确认是否真的需要链接,有时用mount --bind更合适。 -
现象:软链接变成红色且
cat报错 No such file or directory 目标文件被移动或删除了。解决:ls -l查看链接指向的路径,用readlink -f linkname追踪完整路径,修复或重建链接。 -
现象:递归复制目录时符号链接被「实体化」,占用巨大空间 默认
cp -r会追随软链接复制目标内容。解决:改用cp -a(archive 模式)或cp -d(保留链接)。
面试常问
-
硬链接和软链接的区别? 硬链接共享同一个 inode,不能跨文件系统,不能链接目录,删除原文件不影响访问;软链接有自己的 inode,存的是目标路径,可以跨文件系统、可以链接目录,目标删除后悬空失效。
-
为什么删除文件后磁盘空间不释放? 因为文件还被某个进程打开,内核维护的文件描述符引用计数未归零。需找到占用进程(
lsof)并重启或让其释放句柄。MySQL 慢日志、Java 应用的日志文件被 logrotate 删除后常出现此问题。 -
inode 满了但磁盘空间还有,怎么回事? 文件系统有两个上限:数据块(存内容)和 inode(存元数据)。海量小文件(如邮件队列、缓存碎片)会把 inode 耗尽。解决:
df -i查看 inode 使用率,清理小文件或重建文件系统时增大 inode 比例。
自测三问
- 如何创建一个硬链接,并验证它与原文件共享同一个 inode?
- 软链接指向的目标被删除后,软链接本身还在吗?还能访问吗?
- 你发现
/var/log所在分区使用率 100%,但du -sh /var/log显示占用远小于分区大小,可能是什么原因?
小结一句话
Linux 里文件名只是 inode 的「别名」,硬链接是给 inode 多起名字,软链接是给路径写便签——理解 inode 与链接,你就理解了 Linux 文件系统的灵魂。
延伸
- Linux 文件系统结构标准(FHS)
- GNU Coreutils 文档 — ln/cp/mv
- 下一课:文本编辑器——掌握 nano 和 vim,在服务器上直接修改配置。