文件系统基础:inode 的概念
在 Linux 的 ext4、XFS 等文件系统中,每个文件由两部分组成:inode(存储文件元数据)和数据块(存储实际内容)。理解 inode 对于理解硬链接、权限、删除等操作至关重要。
# 查看文件的 inode 编号
ls -li README.md # -i 显示 inode 编号(第一列)
stat README.md # 查看完整元数据(含 inode、链接计数)
# inode 查看示例输出:
# File: README.md
# Size: 1234 Blocks: 8 IO Block: 4096 regular file
# Device: fd00h Inode: 2621441 Links: 1 ← 链接计数为 1
# Access: (0644/-rw-r--r--) Uid: (1000/alice) Gid: (1000/alice)
# Access: 2026-04-01 10:30:00(最后访问时间 atime)
# Modify: 2026-04-01 09:00:00(最后修改时间 mtime)
# Change: 2026-04-01 09:00:00(inode 变更时间 ctime,含权限变更)
# 磁盘 inode 使用情况(inode 用完会导致无法创建新文件!)
df -i # 查看各分区的 inode 使用情况
df -ih # 人类可读格式
inode 数量在格式化时就固定了(ext4 默认每 16KB 一个 inode)。如果系统中有大量小文件(如邮件、日志片段、npm 缓存),即使磁盘空间还有剩余,inode 也可能耗尽,导致无法创建新文件并报错"No space left on device"。用 df -i 检查 inode 使用率,超过 90% 要警惕。
创建文件与目录
# touch:创建空文件或更新时间戳
# 原理:如果文件不存在,创建 0 字节空文件;
# 如果文件存在,更新 atime 和 mtime 为当前时间
touch newfile.txt # 创建空文件
touch file1.txt file2.txt # 一次创建多个空文件
touch -t 202401150900 file.txt # 设置指定时间戳(YYYYMMDDhhmm 格式)
touch -r reference.txt file.txt # 将 file.txt 的时间戳设为与 reference.txt 相同
# mkdir:创建目录
mkdir mydir # 创建单个目录
mkdir dir1 dir2 dir3 # 一次创建多个目录
mkdir -p projects/app/src # 递归创建多级目录(父目录不存在时自动创建,非常常用!)
mkdir -p logs/{2024,2025}/{jan,feb,mar} # 大括号展开:批量创建目录树
mkdir -m 755 secured_dir # 创建时指定权限(755 = rwxr-xr-x)
mkdir -v newdir # 显示创建过程
复制与移动
# cp:复制文件和目录
# 原理:读取源文件数据,在目标位置写入新文件(新 inode)
# 与 mv 不同,cp 消耗额外的磁盘空间
cp source.txt dest.txt # 复制文件(dest 已存在则覆盖,无警告!)
cp source.txt /backup/ # 复制到目录(保留原文件名)
cp -r mydir/ backup/ # 递归复制目录(-r 或 -R 必须!)
cp -i source.txt dest.txt # -i = interactive:覆盖前询问确认
cp -p source.txt dest.txt # -p = preserve:保留原始时间戳、权限和所有者
cp -a src/ dest/ # -a = archive:等同 -r -p,还保留链接(备份首选)
cp -v source.txt dest.txt # -v = verbose:显示每个操作
cp -u source.txt dest.txt # -u = update:只在源文件比目标更新时才复制
cp file1.txt file2.txt /dest/ # 复制多个文件到目录
cp -l source.txt dest.txt # -l = link:创建硬链接而非复制(同一 inode,不占额外空间)
cp -s source.txt dest.txt # -s = symbolic:创建软链接
# mv:移动(重命名)文件和目录
# 原理(同一文件系统):只修改目录项,inode 和数据不变(极快)
# 原理(跨文件系统):先 cp 再 rm,相对较慢
mv oldname.txt newname.txt # 重命名文件
mv file.txt /newlocation/ # 移动到新位置
mv -i source dest # 覆盖前询问(生产环境建议)
mv -n source dest # -n = no-clobber:不覆盖已存在的文件
mv -v *.log /var/log/archive/ # 移动所有日志(显示详情)
mv dir1/ dir2/ # 移动整个目录(不需要 -r)
Linux 默认没有回收站,rm 删除的文件通常无法恢复(取决于文件系统和是否被覆盖)。最佳实践:① 重要操作前先备份;② 用 rm -i 开启交互确认;③ 脚本中删除前先用 ls 确认要删除的范围;④ 安装 trash-cli(sudo apt install trash-cli),用 trash 命令替代 rm。
# rm:删除文件和目录
# 原理:从目录中删除目录项(减少 inode 链接计数),
# 当链接计数归零且没有进程打开该文件时,数据块才被标记为可用
rm file.txt # 删除文件
rm -i file.txt # 删除前询问确认(养成好习惯!)
rm -r mydir/ # 递归删除目录及其内容
rm -rf mydir/ # 强制递归删除(不询问,谨慎!)
rm -v *.tmp # 删除并显示每个被删文件名
rm -- -badfile.txt # 删除以 - 开头的文件(-- 表示选项结束)
# rmdir:只能删除空目录(目录非空会报错)
rmdir emptydir # 删除空目录
rmdir -p a/b/c # 递归删除空目录链(c、b、a 依次删除,前提是都为空)
软链接与硬链接
链接原理与对比
ln -s target link_nameln target link_name# 创建软链接
ln -s /usr/local/bin/python3.12 /usr/bin/python3 # 典型用途:版本别名
ln -s ../config/nginx.conf ./nginx.conf # 相对路径软链接(推荐,可移植)
ln -s /var/log/nginx nginx_logs # 链接整个目录
# 查看链接信息
ls -la nginx.conf # 显示 lrwxrwxrwx ... nginx.conf -> target
readlink nginx.conf # 显示链接目标(不解析相对路径)
readlink -f nginx.conf # 解析为绝对路径(跟踪所有链接直到真实文件)
readlink -e nginx.conf # 绝对路径,且目标必须存在(否则返回空)
# 覆盖已存在的软链接
ln -sf /new/target link_name # -f = force 强制覆盖(更新链接时使用)
# 创建硬链接
ln file.txt hardlink.txt # 创建硬链接(两个名字指向同一 inode)
ls -li file.txt hardlink.txt # 查看:inode 编号相同,link count 都是 2
# 查找悬空软链接(目标不存在的链接)
find . -xtype l # -xtype l 匹配软链接指向不存在目标的链接
find /usr -xtype l 2>/dev/null | head -20 # 查找系统中的悬空链接
创建相对路径软链接时,路径是相对于链接文件所在目录,而不是相对于当前工作目录。例如在 /home/alice 下执行 ln -s ../etc/config ./myconfig,链接 myconfig 会指向 /home/etc/config(相对于 /home/alice),而非 /etc/config。如不确定,建议使用绝对路径。
压缩与归档
tar:打包归档工具
tar 最初是"Tape Archive"(磁带归档),现在是 Linux 上最常用的打包工具。注意:tar 的归档(.tar)和压缩(.tar.gz 等)是两步操作,gz/bz2/xz 只是压缩算法。
# tar 选项速记:
# c = create(创建) x = extract(解压)
# z = gzip(.gz) j = bzip2(.bz2) J = xz(.xz)
# v = verbose(显示) f = file(指定文件)
# t = list(列出内容,不解压)
# ─────── 创建归档 ──────────────────────────────
tar -czf backup.tar.gz mydir/ # gzip 压缩(速度快,最常用)
tar -cjf backup.tar.bz2 mydir/ # bzip2 压缩(压缩率更高,慢)
tar -cJf backup.tar.xz mydir/ # xz 压缩(最高压缩率,最慢)
tar -cf archive.tar file1 file2 dir1 # 只归档,不压缩
# 排除某些文件/目录
tar -czf backup.tar.gz mydir/ --exclude='*.log' --exclude='node_modules'
# 显示进度(大文件有用)
tar -czf backup.tar.gz mydir/ --checkpoint=1000 --checkpoint-action=dot
# ─────── 解压归档 ──────────────────────────────
tar -xzf backup.tar.gz # 解压 gzip 归档到当前目录
tar -xzf backup.tar.gz -C /dest/ # 解压到指定目录(目录必须存在)
tar -xzf backup.tar.gz specific/file # 只解压特定文件(路径需与 tar 内一致)
tar -xzf backup.tar.gz --strip-components=1 # 解压时去掉最外层目录
# 查看归档内容(不解压)
tar -tzf backup.tar.gz | head -20 # 列出归档内文件(前20个)
tar -tJf backup.tar.xz | grep "\.py" # 查找归档中的 Python 文件
# ─────── 更新归档 ──────────────────────────────
tar -uf archive.tar newfile.txt # 将新文件追加到归档(只支持未压缩 .tar)
zip / gzip / bzip2 / xz
# zip/unzip(跨平台,Windows 兼容)
zip archive.zip file1 file2 # 压缩文件
zip -r archive.zip mydir/ # 递归压缩目录
zip -e secret.zip file.txt # 加密压缩(会提示输入密码)
unzip archive.zip # 解压到当前目录
unzip archive.zip -d /dest/ # 解压到指定目录
unzip -l archive.zip # 列出内容(不解压)
unzip -p archive.zip file.txt # 解压单个文件到标准输出
# gzip:只压缩单个文件(不归档)
gzip file.txt # 压缩(原文件会被替换为 file.txt.gz)
gzip -k file.txt # -k = keep:保留原文件
gzip -d file.txt.gz # 解压(或 gunzip file.txt.gz)
gzip -9 file.txt # 最高压缩级别(1-9,默认 6)
zcat file.txt.gz # 直接查看 gzip 压缩文件内容(不解压)
zgrep "error" app.log.gz # 在压缩日志中搜索(非常实用!)
# 压缩率对比(以 100MB 文本为例):
# gzip -1: 速度最快,压缩率低
# gzip -9: 速度慢,压缩率中
# bzip2: 压缩率高于 gzip,速度较慢
# xz -9: 压缩率最高,速度最慢(约 3-5x 于 gzip)
file 和 stat:文件类型与元数据
# file:识别文件类型(通过"魔数"识别,不依赖文件扩展名!)
# 原理:读取文件开头的几个字节(magic bytes),对照数据库识别格式
# 例如:PDF 文件以 %PDF 开头,PNG 以 89 50 4E 47 开头
file image.jpg # JPEG image data, JFIF standard 1.01
file script.py # Python script, ASCII text executable
file mybinary # ELF 64-bit LSB executable, x86-64, dynamically linked
file archive.tar.gz # gzip compressed data, was "archive.tar"
file /dev/sda # block special (8/0)
file renamed_doc.pdf # 即使改名为 .pdf,file 也能识别真实格式
file -i image.jpg # 显示 MIME 类型(image/jpeg; charset=binary)
file * # 识别当前目录所有文件类型
# stat:查看文件详细元数据
stat README.md
# 输出解读:
# File: README.md ← 文件名
# Size: 1234 ← 文件大小(字节)
# Blocks: 8 ← 占用的 512B 数据块数量
# IO Block: 4096 ← 文件系统的块大小
# regular file ← 文件类型
# Device: fd00h/64768d ← 设备编号
# Inode: 2621441 ← inode 编号
# Links: 1 ← 硬链接计数
# Access: (0644/-rw-r--r--) ← 权限(八进制 + 符号表示)
# Uid: (1000/alice) ← 所有者 UID 和用户名
# Gid: (1000/alice) ← 所属组 GID 和组名
# Access: 2026-04-01 ... ← atime(最后访问时间)
# Modify: 2026-04-01 ... ← mtime(最后内容修改时间)
# Change: 2026-04-01 ... ← ctime(元数据变更时间,含权限/所有者变更)
默认情况下,每次读取文件都会更新 atime(访问时间),这意味着每次 cat、ls、程序读取配置文件都会触发磁盘写操作(更新 inode),对高负载服务器影响显著。现代 Linux 内核默认使用 relatime 挂载选项(只有 atime 早于 mtime 时才更新 atime),或使用 noatime(完全不更新 atime)以提升 I/O 性能。
rsync:增量同步利器
rsync(Remote Sync)是比 cp 更强大的文件同步工具,核心优势是增量传输——只传输变化的部分,不是每次都全量复制。
# rsync 基本语法:rsync [选项] 源路径 目标路径
# 重要:源路径末尾的 / 意义不同!
# rsync -a src/ dest/ : 将 src 目录内的内容复制到 dest 内
# rsync -a src dest/ : 将 src 目录本身复制到 dest 内(dest/src/...)
# 本地目录同步
rsync -av src/ dest/ # -a=archive(保留权限时间等)-v=verbose
rsync -avz src/ dest/ # -z=compress:传输时压缩(网络同步必加)
rsync -av --progress src/ dest/ # 显示传输进度和速度
rsync -av --delete src/ dest/ # --delete:删除目标中源没有的文件(镜像同步)
# 排除文件
rsync -av --exclude='*.log' --exclude='node_modules/' src/ dest/
rsync -av --exclude-from='rsync-exclude.txt' src/ dest/ # 从文件读取排除规则
# 增量备份:--backup-dir 将修改的旧文件保存到备份目录
rsync -av --backup --backup-dir=/backup/$(date +%Y%m%d) src/ dest/
# SSH 远程同步(最常用场景)
rsync -avz -e ssh src/ user@server:/remote/path/ # 上传到远程服务器
rsync -avz user@server:/remote/path/ /local/dest/ # 从远程服务器下载
rsync -avz -e "ssh -p 2222" src/ user@server:/dest/ # 指定 SSH 端口
# 模拟运行(不实际修改,查看会做什么)
rsync -av --dry-run src/ dest/ # --dry-run 或 -n,强烈建议在生产环境先试运行
文件批量操作技巧
# 批量重命名(使用 rename 命令)
rename 's/\.txt$/.md/' *.txt # 将所有 .txt 重命名为 .md(Perl 版本)
rename 's/IMG_/photo_/g' IMG_* # 批量替换文件名前缀
# 使用 for 循环批量操作(通用方案)
for f in *.log; do
cp "$f" "/backup/${f%.log}_$(date +%Y%m%d).log" # 复制并加日期后缀
done
# 使用 find + exec 批量操作
find . -name "*.jpg" -exec cp {} /photos/ \; # 复制所有 jpg 文件
find . -name "*.tmp" -mtime +7 -delete # 删除 7 天前的 tmp 文件
find . -empty -type f -delete # 删除所有空文件
# 安全地处理含空格的文件名
find . -name "*.txt" -print0 | xargs -0 rm # -print0 和 -0 用 NULL 分隔,处理空格
Linux 文件操作的核心要点:① 理解 inode——文件名存在目录中,inode 存元数据,链接计数归零才真正删除;② cp -a 用于备份(保留权限时间),mv 在同一文件系统内只修改目录项(极快);③ 软链接(ln -s)灵活可跨文件系统,硬链接不能链接目录;④ 备份用 tar -czf,增量同步用 rsync -avz;⑤ file 命令通过魔数识别真实格式,不依赖扩展名;⑥ 使用 rm -i 或 trash 避免误删文件。