当你需要整理目录、原地重命名文件,或在不同的位置之间移动数据时,mv 命令是 Linux 中最常用的工具之一。
在 Linux 中移动文件,使用 mv SOURCE DESTINATION。例如,mv file.txt /tmp/ 会把 file.txt 移动到 /tmp 目录中。
mv 命令(move 的缩写)用于在文件系统中移动、重命名文件与目录。本教程涵盖最常见的 mv 使用场景,从基础移动、创建备份,到跨文件系统移动。
mv 命令语法
mv 命令的基本语法如下:
mv [OPTIONS] SOURCE DESTINATION
SOURCE 可以是一个或多个文件/目录,DESTINATION 可以是一个文件或目录。
mv 的具体行为取决于源与目标:
- 若
SOURCE是文件、DESTINATION是目录,则文件被移入该目录。 - 若
SOURCE是文件、DESTINATION也是文件,则源文件被重命名为目标文件名。 - 若给出多个
SOURCE,DESTINATION必须是目录。 - 若
SOURCE是目录且DESTINATION不存在,则SOURCE被重命名为DESTINATION;若DESTINATION已存在且为目录,则SOURCE被移入其中。
移动文件或目录时,你需要对源位置和目标位置都拥有写权限,否则会看到权限拒绝错误。
移动文件
将文件从当前工作目录移动到另一个目录:
mv file1 /tmp
移动文件的同时在目标位置为其指定新名称:
mv file1 /tmp/newname.txt
如果文件名以短横线 - 开头,可在源文件名前加上 --,这样 mv 就不会把它当作选项来解析:
mv -- -file.txt /tmp/
重命名文件
在同一目录内重命名文件,只需把新名称作为目标位置:
mv file1 file2
这会在当前目录下将 file1 重命名为 file2。
移动目录
移动目录的语法与移动文件相同。若 dir2 已存在,命令会把 dir1 移入 dir2 内部;若 dir2 不存在,则 dir1 被重命名为 dir2。
mv dir1 dir2
目录内的所有内容会随之一起移动。与 cp 命令需要 -R 才能复制嵌套内容不同,这里无需额外的递归选项。
若想在移动目录的同时保留其名称,把父目录作为目标位置即可:
mv project /opt
这会创建 /opt/project。目标路径末尾的斜杠没有影响,因此 mv project /opt/ 效果完全相同。
在脚本中需要特别留意「移动」与「重命名」的切换,因为同一条命令会根据目标是否存在产生两种不同的结果。如果你执行 mv project /opt/project,而 /opt/project 已经存在,结果会变成 /opt/project/project。当脚本绝不允许把源目录嵌入已存在的目录时,应使用 -T 选项配合 mv。
mv -T project /opt/project
现在 mv 不会再嵌套。如果目标目录已存在且其中仍有文件,命令会直接报错而不会执行合并。
mv: cannot move 'project' to '/opt/project': Directory not empty
对目录就地重命名,普通的两参数形式就足够了。关于批量重命名目录的其它工具,请参阅「移动多个文件」一节。
移动多个文件
移动多个文件时,把它们列在目标目录之前:
mv file1 file2 file3 dir1
也可以使用模式匹配。例如,把所有 .pdf 文件移动到 ~/Documents 目录:
mv *.pdf ~/Documents
-t(target directory,目标目录)选项在把 mv 与 find 等命令结合使用时非常方便:
find /tmp -name '*.log' -exec mv -t /var/log/archive {} +
这会把在 /tmp 中找到的所有 .log 文件移动到 /var/log/archive。
-t
移动目录内容
把一个目录的内容清空到另一个目录中(而本身不移动该目录),是常见需求,但直观的命令有个坑。
mv olddir/* newdir/
可见文件会进入 newdir,但 * 永远不会匹配以点开头的名称,因此 .bashrc、.config 之类的文件会留在原地。要包含隐藏文件,需先启用 dotglob(见下文)。
shopt -s dotglob
mv olddir/* newdir/
shopt -u dotglob
最后一行把该选项重新关闭,这样同一 shell 会话中的后续命令行为符合预期。如果保留 dotglob 开启状态,之后执行 rm * 可能会删除超出预期的文件。
要移动除某一个文件之外的所有内容,开启 extglob 并对想要保留的文件名取反:
shopt -s extglob
mv !(keep.txt) /backup/
!(pattern) 形式匹配所有「不匹配该模式」的名称。用 | 分隔多个名称即可排除不止一个文件,例如 !(keep.txt|notes.md)。与 * 通配符不同,该扩展在启用 dotglob 时同样会包含隐藏文件,并且可以直接配合 mv 使用。
mv 命令选项
mv 命令支持多个选项,可改变其默认行为。
在部分 Linux 发行版中,mv 被设置了带默认选项的别名。例如,某些系统把 mv 别名为 mv -i。可以用 type 命令检查别名:
type mv
如果 mv 被设置了别名,输出会类似这样:
mv is aliased to `mv -i'
如果给出相互冲突的选项,最后出现的那一个优先生效。
覆盖前提示(-i)
默认情况下,如果目标文件已存在,会被直接覆盖且不发出警告。如需在覆盖前确认,可使用 -i(interactive,交互)选项:
mv -i file1 /tmp
mv: overwrite '/tmp/file1'?
输入 y 确认覆盖,输入 n 跳过。
强制覆盖(-f)
当尝试覆盖只读文件时,mv 默认会要求确认:
mv: replace '/tmp/file1', overriding mode 0400 (r--------)?
要跳过提示并强制覆盖,使用 -f 选项:
mv -f file1 /tmp
当你需要覆盖多个只读文件而又不想逐个确认时,这个选项非常有用。
禁止覆盖(-n)
-n(no-clobber)选项会让 mv 绝不覆盖已存在的文件:
mv -n file1 /tmp
如果 file1 在 /tmp 中已存在,则保持原样不动;否则正常移动。
跳过会被视为失败,而不是静默地什么都不做。自 coreutils 9.2 起,mv -n 在遇到跳过文件时会以非零状态退出,这对使用 set -e 的脚本尤为关键。下文介绍的 --update=none 形式可避免这一失败。
只移动更新的文件(-u)
-u(update)选项仅在源文件比目标文件更新,或目标文件不存在时才进行移动:
mv -u file1 /tmp
当 /tmp/file1 的修改时间相同或更新时,mv 会静默跳过该文件,但仍以成功状态退出。这非常适合做增量同步。
GNU coreutils 9.3 及更高版本还支持更长的 --update=UPDATE 形式,以便更精细地控制:
- older —— 当目标文件的修改时间相同或更新时跳过。这是默认行为,也是普通
-u的效果。 - all —— 移动每一个文件,即不使用
-u时的普通行为。 - none —— 跳过所有已存在的目标文件,且不报告错误。
- none-fail —— 跳过所有已存在的目标文件,并以失败状态退出。
none 值在脚本中很实用:当「目标文件已存在」是可接受的结果、而非错误时尤其方便:
mv --update=none file1 /tmp
--update=UPDATE
创建备份(-b)
如果目标文件已存在,可在覆盖之前使用 -b 选项为其创建备份:
mv -b file1 /tmp
备份文件的名称与原文件相同,只是在末尾追加一个波浪号(~)。可用 ls 命令验证:
ls /tmp/file1*
/tmp/file1 /tmp/file1~
若想使用自定义后缀而非 ~,可使用 --suffix:
mv -b --suffix=.bak file1 /tmp
这会创建一个名为 file1.bak 的备份文件。
--backup=CONTROL 形式决定了备份文件的命名方式。mv 接受以下取值:
- none 或 off —— 完全不创建备份。
- numbered 或 t —— 创建带编号的备份,例如
file1.~1~、file1.~2~。 - existing 或 nil —— 若已存在编号备份则使用编号备份,否则使用简单备份。
- simple 或 never —— 始终创建简单备份(仅追加后缀)。
当同一个文件被反复移入目标位置时,编号备份更安全,因为每次移动都会保留先前版本,而不是覆盖掉 file1~。
mv --backup=numbered file1 /tmp
显示详细输出(-v)
-v 选项会在每个文件被移动时打印其名称:
mv -v file1 file2 /tmp
renamed 'file1' -> '/tmp/file1' renamed 'file2' -> '/tmp/file2'
跨文件系统移动文件
在同一文件系统内移动文件时,mv 只是更新目录项,而不复制任何数据。这速度极快,并且会保留文件的全部属性。
当把文件移动到不同的文件系统(例如从硬盘移动到 U 盘)时,跨设备的重命名无法完成,因此 mv 会先把数据复制到新位置,再删除原始文件。该复制行为等同于 cp -a,会一并保留权限与时间戳。
故障排查
mv: cannot stat 'file': No such file or directory —— 源路径不正确,或文件已被移动。请用 ls 核对路径,并对包含空格的路径加引号。
mv: cannot move ...: Permission denied —— 你对源目录或目标目录没有写权限。请用 ls -ld 检查权限,仅在确有需要时再使用 sudo。
跨文件系统移动时,mv 会先复制数据再删除源文件。若过程中断,请在手动删除任何内容之前,先核对源路径与目标路径是否一致。
mv: target 'dir' is not a directory —— 当你传入多个源文件、而目标位置并非一个已存在的目录时会出现。请先创建目标目录,或只传入单个源文件。
快速参考
如需可打印的快速参考,请参阅 mv 命令速查表。
| 命令 | 说明 |
|---|---|
mv file1 /tmp | 将文件移动到目录 |
mv file1 file2 | 重命名文件 |
mv dir1 dir2 | 移动或重命名目录 |
mv file1 file2 dir/ | 将多个文件移动到目录 |
mv *.txt dir/ | 移动匹配模式的文件 |
mv -i file1 /tmp | 覆盖前提示确认 |
mv -f file1 /tmp | 强制覆盖,不提示 |
mv -n file1 /tmp | 不覆盖已存在的文件 |
mv -u file1 /tmp | 仅当源文件更新时移动 |
mv -b file1 /tmp | 覆盖前为原目标创建备份 |
mv --backup=numbered file1 /tmp | 保留带编号的目标备份 |
mv -v file1 /tmp | 移动时打印每个文件名 |
mv -t dir/ file1 file2 | 先指定目标目录 |
mv -T dir1 /opt/dir1 | 将目标视为路径而非容器 |
常见问题
mv 用于移动或重命名文件,原文件会从源位置移除;cp 则会创建副本,原文件保留在原地。
会。在同一文件系统内移动时,mv 只是重写目录项,文件本身没有任何改变。跨文件系统移动时,复制行为与 cp -a 一致,会在目标允许的范围内保留权限、时间戳、符号链接及所有者。
通配符 * 默认不匹配隐藏文件。移动前在 Bash 中执行 shopt -s dotglob 让模式包含它们,移动后再执行 shopt -u dotglob 关闭。完整用法见「移动目录内容」一节。
使用 -n 选项:mv -n file1 /tmp。若目标文件已存在,mv 不做任何操作。
不可以。mv 命令没有撤销功能。如果你使用了 -b,可以手动恢复备份文件;否则只能自己把文件移回原来的位置。
总结
mv 用于移动与重命名;当可能发生覆盖时加上 -i 或 -n,当脚本绝不允许把目录嵌入已存在的目录时加上 -T。若需要保留原文件不动,请改用 cp 命令进行复制。