有时我有两棵树,它们曾经具有相同的内容,但已经不同步了(因为我移动了磁盘或其他原因)。一个很好的例子是我从 Fedora 镜像上游包的树。
我想通过将所有文件从 tree1 移动到 tree2 来再次合并这两棵树。
通常我这样做:
rsync -arv tree1/* tree2
然后删除tree1。
但是,这需要大量的时间和磁盘空间,而且这样做会容易得多:
mv -r tree1/* tree2
换句话说,递归移动。它会更快,因为首先它甚至不会复制,只需移动 inode,其次我不需要最后删除。
这存在吗?
作为测试用例,请考虑以下命令序列:
$ mkdir -p a/b
$ touch a/b/c1
$ rsync -arv a/ a2
sending incremental file list
created directory
./
b/
b/c1
b/c2
sent 173 bytes received 57 bytes 460.00 bytes/sec
total size is 0 speedup is 0.00
$ touch a/b/c2
什么命令现在具有将 a/b/c2 移动到 a2/b/c2 然后删除 a 子树的效果(因为其中的所有内容都已经在目标树中)?
根据 gnu 的 mv(1) 联机帮助页
mv
:-u, --update move only when the SOURCE file is newer than the destination file or when the destination file is missing
建议
mv -uf dir1/* dir2/
移动(子)目录,而不是每个文件。你可以尝试使用find
或类似的东西
才不是
工作?
午夜指挥官(mc)也很适合这种东西。使用 CTRL-t 标记文件,按 F6,当它要求覆盖目标文件时,如果要覆盖旧文件,请选择更新。
您可以使用“cp -l & rm”进行设备内移动:
-l
使用cp
硬链接而不是复制,(这也可以防止跨设备操作)--backup=numbered
cp
用于备份目标目录中的现有文件并且要小心这两个问题:
&&
来防止删除未复制的数据。(在 corss-device 情况下cp
以“”状态退出1
,至少对于 GNU coreutils).
以 " " in开头的文件tree1
,如果有,您将丢失它们。哈维尔对 find 的回答效果很好,只是它不会删除原始目录。在末尾添加:
呃
或者干脆
应该足够了,当有太多条目时,您可能会在某个时候遇到麻烦
dir1
。应该是不错的选择
两者都不是必需的,因为 mv 只需要 2 个选项(源目标),因此在这种情况下您将不得不忍受大量进程。
这不会移动隐藏的文件或文件夹,但您的原始示例也不会。
我认为 mv 没有做你认为它做的事情。
一个 unix 文件系统有 3 个组件:
一个目录入口指向一个 inode。
inode 有关于文件的元数据(它是文件、目录还是命名管道?谁拥有它?权限是什么?那个 inode 使用什么块?
块是实际包含文件内容的东西。
所以——当你“mv”一个文件时,你真正要做的就是取消第一个目录条目的链接并将它重新链接到其他地方。
没有数据被复制/复制。您创建链接 snoopy,然后创建链接 woodstock,然后删除链接 snoopy。(目录的情况有些不同,因为通常您不能创建硬链接目录,但即便如此,“链接”名称也会发生变化)。
如果您要从一个文件系统迁移到另一个文件系统怎么办?在过去, mv 只会抛出一个错误并明确表明您不能将文件从一个文件系统移动到另一个文件系统。这些天来,似乎 mv 默默地复制数据然后删除原始数据。
在过去,由于您无法将数据从一个文件系统移动到另一个文件系统,因此您养成了使用习惯用法的习惯,例如
焦油 -cf - 。| (cd /new/location && tar -xf -)
然后你删除旧数据。使用 tar 的部分原因是,在过去,cp 会破坏诸如“这是一个符号链接”和“这是一个硬链接”之类的元数据,而您只需将该文件的新副本作为常规文件获取。即使如此,您仍需要提供“cp”标志来告诉它保留这种结构。
如果数据从一个文件系统到另一个文件系统,则无法避免“移动”大量数据。使用花哨的 new move 或 rsync 或 tar 或 cpio 都没有关系。
但是,如果您将所有数据保存在同一个文件系统中,则:
mv /filesystem-1/big/directory* /filesystem-1/big2/
这将非常快,因为它只是更改目录条目而不实际移动任何真实数据。
还有其他问题在起作用,例如如果新位置以及源位置中已经存在文件/目录,该怎么办?
这个线程中的答案都不适合我的用例,所以我自己想出了一个作为shell script的答案。
它的核心是这个功能:
您可以这样调用(对于 OP 的用例):
如果源是目标尚不存在的文件或目录,则只需将其移动到
mv -u
. 如果源是目标已经存在的目录,它会遍历其内容并递归执行相同的检查,然后对其每个成员进行移动|递归。我使用它
-u
是因为我只需要更新旧文件和保持新文件不变,但您可以将其替换-f
为无条件移动或-i
交互式移动。或者rm -rf
在脚本完成移动内容后不更改任何内容,只更改您的源代码。