CodeRoad Map 程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。
由 程序员柏风 独立维护 · 关于作者
学习 更多
文件与目录 — CodeRoadMap
文件与目录
这一课你将能做到
熟练使用 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/logscp 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_dirmv /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 文件系统的灵魂。
延伸