mv
mv(move)是 Linux 中唯一一个让你"瞬间移动"任意大小文件的命令——同文件系统内它不复制数据,只改一个指针。这层原子性让它成为安全部署、事务性文件更新和批量重命名的基建工具。
速查卡片
| 需求 | 命令 |
|---|---|
| 重命名文件 | mv old.txt new.txt |
| 移动文件到目录 | mv file.txt /target/dir/ |
| 移动多个文件到目录 | mv a.txt b.txt /target/dir/ |
| 覆盖前确认 | mv -i src dst |
| 强制覆盖不提示 | mv -f src dst |
| 不覆盖已存在文件 | mv -n src dst |
| 仅源更新时才移动 | mv -u src dst |
| 覆盖前备份旧文件 | mv -b src dst(旧文件 → dst~) |
| 详细输出 | mv -v src dst |
| 交换两个文件 | mv --exchange a.txt b.txt |
| 拒绝跨文件系统(保原子性) | mv --no-copy /tmp/f /mnt/usb/f |
| 安全部署(原子替换) | make > output.new && mv output.new output |
概述
mv 兼具重命名和移动两种功能。它的实现取决于源和目标是否在同一文件系统:
- 同文件系统:调用
rename(2)系统调用——只修改目录项指针,不复制数据,inode 不变,整个操作是原子的 - 跨文件系统:回退为
cp+unlink——完整复制数据后删除源
这种双重行为是理解 mv 一切特性的起点。选项方面,mv 继承了 cp 的覆盖控制家族(-i/-f/-n/-u/-b),并独有 --exchange(交换)和 --no-copy(拒绝跨设备)两个选项。
命令签名
bash
# 形式一:重命名/移动单个文件
mv [OPTION]... [-T] SOURCE DEST
# 形式二:多文件移动到目录
mv [OPTION]... SOURCE... DIRECTORY
# 形式三:-t 目标在前(GNU 扩展)
mv [OPTION]... -t DIRECTORY SOURCE...-T:强制将 DEST 视为普通文件(即使它是已存在的目录也不把源移进去)
基础用法
示例一:最简——重命名
bash
$ mv oldname.txt newname.txt
$ mv file.txt /target/dir/ # 移动到目录,保留原名
$ mv file.txt /target/dir/newname.txt # 移动并改名示例二:批量移动
bash
$ mv *.log /archive/logs/ # 所有 .log 文件移入 /archive/logs/
$ mv dir1 dir2 dir3 /target/ # 多个目录一起移动示例三:覆盖控制
bash
$ mv -i src.txt dst.txt # 覆盖前确认
mv: overwrite 'dst.txt'? y
$ mv -n src.txt dst.txt # 不覆盖,静默跳过
$ mv -u src.txt dst.txt # 仅 src 更新时才覆盖
$ mv -b src.txt dst.txt # 覆盖前备份
# dst.txt 旧内容 → dst.txt~-i/-f/-n 同时指定时,最后一个生效:
bash
$ mv -f -i src.txt dst.txt # -i 胜出 → 提示确认
$ mv -i -n src.txt dst.txt # -n 胜出 → 静默跳过示例四:原子文件更新——mv 最有价值的用法
bash
# ❌ 非原子:写入过程中文件可能不完整
echo "new config" > /etc/app.conf
# ✅ 原子:先写入临时文件,再原子替换
echo "new config" > /etc/app.conf.new
mv /etc/app.conf.new /etc/app.conf在 mv 发生的瞬间,/etc/app.conf 从旧内容"跳变"到新内容——不存在观察者看到文件为空的窗口。这是守护进程热重载、配置文件部署的标准模式。
核心原理:rename(2) 原子操作
mv 与 cp 的本质区别在于底层系统调用:
┌─ 同文件系统 ──→ rename(2) 原子操作
mv SOURCE DEST ────┤
└─ 跨文件系统 ──→ cp + unlink(非原子)同文件系统内(rename(2))
| 特性 | 行为 |
|---|---|
| 数据复制 | 无——只修改源和目标所在目录的目录项 |
| inode | 完全不变 |
| 硬链接 | 所有指向该 inode 的硬链接仍有效 |
| 已打开的文件 | 持有文件描述符的进程不受影响(通过 inode 访问) |
| 速度 | 瞬间完成,与文件大小无关 |
| 原子性 | 不可能出现中间状态——要么旧路径存在,要么新路径存在 |
跨文件系统(cp + unlink)
| 特性 | 行为 |
|---|---|
| 数据复制 | 需要完整读取源、写入目标 |
| inode | 变化(新文件系统上的新 inode) |
| 硬链接 | 旧硬链接仍指向原 inode,但那是源位置的数据 |
| 速度 | 等于 cp + rm 的时间 |
| 原子性 | 非原子——中断后目标有不完整文件,源仍存在 |
--no-copy —— 强制原子性
bash
$ mv --no-copy /tmp/file.txt /mnt/usb/
mv: cannot move '/tmp/file.txt' to '/mnt/usb/file.txt': cross-device move not permitted在需要保证原子性的脚本中,--no-copy 让跨文件系统的 mv 直接失败,而不是静默回退为复制。
--exchange —— mv 独有的交换操作
bash
$ mv --exchange a.txt b.txt
# a.txt ↔ b.txt 原子互换,没有中间状态这是唯一一个能原子交换两个文件路径的命令。典型场景:
bash
# 原子切换符号链接(蓝绿部署)
ln -s /srv/app-v2 /srv/app-new
mv --exchange /srv/app /srv/app-new # /srv/app → v1, /srv/app-new → v2
# 实际上就是交换了符号链接的目标,然后删掉旧版
# 批量重命名中配合使用目录操作的特殊规则
不能把目录移入自身
bash
$ mv work/ work/sub/
mv: cannot move 'work/' to a subdirectory of itself, 'work/sub/work'不能合并目录
bash
$ mv src_dir/ dst_dir/
# 如果 dst_dir 已存在且非空且与 src_dir 不同名 → 报错:
mv: cannot overwrite directory 'dst_dir' with non-directory
# 或:
mv: cannot move 'src_dir' to 'dst_dir/src_dir': Directory not emptymv 不会递归合并目录内容。需要合并时用 cp -r + rm -r 或 rsync --remove-source-files。
同文件系统 vs 跨文件系统移动目录
| 同文件系统 | 跨文件系统 | |
|---|---|---|
| 实现 | 修改父目录的目录项 | 递归 cp + 递归 rm |
| 速度 | 瞬间(与目录内文件多少无关) | 与目录总大小成正比 |
| inode | 目录本身的 inode 不变 | 变化 |
符号链接行为
mv 操作符号链接本身,从不跟随:
bash
$ ln -s /etc/hostname link
$ mv link /tmp/
$ ls -l /tmp/link
lrwxrwxrwx ... /tmp/link -> /etc/hostname # 链接被移动,目标不变- 如果目标是符号链接指向目录 →
mv报错(歧义),需-T强制 - 源和目标都是指向同一文件的符号链接 → 允许(POSIX 规定)
选项互斥规则
bash
mv -i -n src dst # -n 胜出:静默跳过
mv -n -i src dst # -i 胜出:提示确认
mv -f -n src dst # -n 胜出:静默跳过
mv -f -i src dst # -i 胜出:提示确认(POSIX 和 GNU 都是后者胜出)常用命令组合
bash
# 批量重命名——仅当目标不存在
for f in *.jpeg; do mv -n "$f" "${f%.jpeg}.jpg"; done
# 配合 find 批量移动
find . -name '*.log' -mtime +30 -exec mv -t /archive/logs/ {} +
# 备份式移动
mv -bv /tmp/staging/* /srv/app/
# 原子部署
go build -o server.new && mv server.new server && systemctl restart myserver
# 蓝绿原子切换
ln -sfn /srv/app-green /srv/app-live
# (mv --exchange 是另一种方式)脚本安全模式
bash
# ✅ 原子更新
generate_config > /etc/app.conf.new
mv /etc/app.conf.new /etc/app.conf
# ✅ 仅源更新时才移动(避免不必要的时间戳变更)
mv -u /tmp/cache/* /var/cache/
# ✅ 不覆盖已有
mv -n /tmp/staging/* /srv/deploy/
# ✅ 覆盖前备份
mv -b config.new /etc/app/config
# ✅ 强制原子,失败即退出
mv --no-copy /tmp/staging/output /srv/prod/output || {
echo "跨设备,无法原子移动" >&2
exit 1
}常见报错
| 错误信息 | 原因 | 解决 |
|---|---|---|
mv: cannot move 'x' to a subdirectory of itself | 尝试把目录移入自己的子目录 | 检查路径,确认目标不在源内 |
mv: cannot overwrite directory with non-directory | 目标是非目录,源是目录 | 删除目标文件或选不同目标路径 |
mv: cannot overwrite non-directory with directory | 目标是目录,源是非目录 | 删除目标目录或选不同目标路径 |
mv: Directory not empty | 目标目录已存在且非空 | 先 rm -r 目标或选不同路径 |
mv: 'src' and 'dst' are the same file | 源和目标解析为同一文件 | 检查路径;同文件移动时加后缀 |
mv: will not overwrite just-created | 同批次中重名文件重复移动 | find 结果中有同名字文件,用 rsync |
mv: cross-device move not permitted | --no-copy 阻止了跨文件系统移动 | 去掉 --no-copy 或用 cp+rm |
| mv: Permission denied | 源父目录无写权限(删除旧名需要)或目标目录无写权限 | chmod 或 sudo |
注意事项
- 目录不能合并——这是
mv与cp -r最大的使用差异 - 跨文件系统移动大目录耗时很长——等于完整复制后再删除,中断不会丢数据(源还在)
mv操作的是目录项,不是数据——同文件系统内已打开的文件继续有效,通过/proc/<pid>/fd/<n>仍可访问- 尾部斜杠被自动去掉——与
cp不同,mv中src/和src行为一致 - 源和目标是同一文件时:GNU
mv诊断但退出码 0;POSIXrename()检测到同 inode 则无事发生
常见误区
| ❌ 误区 | ✅ 正解 |
|---|---|
"mv 就是先 cp 再 rm" | 同文件系统内只改目录项指针——不复制数据,inode 不变,是原子操作 |
"mv 之后 inode 会变" | 同文件系统内 inode 完全不变,已打开文件的进程无感知 |
"mv dir/ dst/ 和 mv dir dst 有区别" | GNU mv 自动去掉尾部斜杠,行为一致 |
"可以 mv 合并两个目录" | mv 不能合并——要用 cp -r + rm -r 或 rsync |
"mv -f 可以覆盖非空目录" | -f 只控制覆盖提示,不改变目录合并限制 |
| "跨文件系统 mv 也是原子的" | 跨设备回退为 cp+rm,非原子 |
mv vs cp 场景速查
| 场景 | mv | cp |
|---|---|---|
| 重命名 | ✅ | ❌ |
| 同设备移动 | ✅ 瞬间、原子 | ❌ |
| 保留 inode/硬链接 | ✅ | ❌ |
| 保留源文件 | ❌ | ✅ |
| 交换两个文件 | ✅ --exchange | ❌ |
| 合并目录 | ❌ | ✅ cp -r |
| 复制到远程 | ❌ | ❌(用 scp/rsync) |
版本与规范信息
| 事件 | 说明 |
|---|---|
| POSIX 纳入 | Issue 2 |
rename(2) 原子性 | POSIX.1-2001 明确规定 |
GNU --exchange | coreutils ≥ 9.0 |
GNU --no-copy | coreutils ≥ 9.0 |
| GNU coreutils 当前 | 9.11(2026-04) |
macOS 特别说明
- macOS 的
mv来自 BSD,无--exchange、--no-copy、-t、-T等 GNU 扩展 - BSD
mv的-f/-i/-n行为与 GNU 一致 - macOS 文件系统(APFS)同一宗卷内
mv同样是原子的 - macOS 无
--strip-trailing-slashes(但 BSD 版本也不依赖尾部斜杠歧义)
来源
| 来源 | 链接 |
|---|---|
| GNU coreutils mv man page | https://man7.org/linux/man-pages/man1/mv.1.html |
| GNU coreutils 官方文档 | https://www.gnu.org/software/coreutils/mv |
| rename(2) 系统调用 | https://man7.org/linux/man-pages/man2/rename.2.html |
| POSIX mv 规范 | https://pubs.opengroup.org/onlinepubs/9699919799/utilities/mv.html |
| Baeldung: mv 指南 | https://www.baeldung.com/linux/mv |
| 整理时间 | 2026-07-14 |
