当你需要整理目录、原地重命名文件,或在不同的位置之间移动数据时,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 也是文件,则源文件被重命名为目标文件名。
  • 若给出多个 SOURCEDESTINATION 必须是目录。
  • 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,目标目录)选项在把 mvfind 等命令结合使用时非常方便:

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 命令进行复制。