用 mv 合并文件夹?

Dom*_*que 195 directory rename

如果我使用mv将名为“文件夹”的文件夹移动到已经包含“文件夹”的目录,它们会合并还是会被替换?

n.s*_*.st 150

mv无法合并或覆盖目录,它会失败并显示消息"mv: cannot move 'a' to 'b': Directory not empty",即使您正在使用该--force选项。


您可以使用其他工具(如rsyncfind、 甚至cp)来解决此问题,但您需要仔细考虑其含义:

  • rsync可以将一个目录的内容合并到另一个目录中(理想情况下,使用--remove-source-files1选项仅安全删除那些成功传输的源文件,-a如果愿意,还可以使用通常的权限/所有权/时间保留选项)
    ……但这是一个完整的复制操作,因此可能会占用大量磁盘空间。
  • 当前首选选项:您可以组合rsync--link-dest=DIR选项(在可能的情况下创建硬链接而不是复制文件内容)并--remove-source-files获得与常规mv.
    为此,--link-dest需要给出目录的绝对路径(或从目标的相对路径)。
    ...但这--link-dest在以一种意想不到的方式使用(这可能会也可能不会引起并发症),需要知道(或确定)源的绝对路径(作为 的参数--link-dest),并再次留下一个空的目录结构作为清理每1 .
  • 您可以使用find在目标上按顺序重新创建源目录结构,然后单独移动实际文件
    ……但这必须多次遍历源并可能遇到竞争条件(在多步骤过程中在源上创建新目录)
  • cp可以创建硬链接(简单地说,额外的指向同一个现有文件的指针),这会创建一个非常类似于合并的结果mv(并且非常具有 IO 效率,因为只创建了指针并且不需要复制实际数据)
    ……但是这个再次遭受可能的竞争条件(源中的新文件被删除,即使它们在上一步中没有被复制)

这些变通方法(如果有的话)中的哪一个是合适的,将在很大程度上取决于您的特定用例。
与往常一样,在执行任何这些命令之前请三思,并做好备份。


1:请注意,rsync --remove-source-files不会删除任何目录,因此您必须在find -depth -type d -empty -delete之后执行类似操作才能摆脱空的源目录树。

  • rsync 的缺点是它实际上复制了数据,而不仅仅是更改硬链接,如果您处理大量数据,这可能会占用大量资源。 (42认同)
  • @Keith 请注意,`--delete` 仅删除源目录中不存在的文件*在目标目录中*。 (9认同)
  • @JonathanMayer rsync 作为几个与硬链接相关的功能。例如,您可以只使用 ``-H`` 函数保留硬链接,或者您可以使用 ``--link-dest`` 硬链接目标中的文件。不过,在使用它们之前请参阅手册页。 (2认同)
  • @allo `--link-dest` 是个好主意!从手册页来看,它最初不是用于在源目录上操作的,但它在快速测试中完美无瑕(并且非常高效)。我现在会推荐它,但我会感谢任何有关并发症的反馈。 (2认同)

faz*_*zie 111

rsync -av /source/ /destination/
(after checking)
rm -rf /source/
Run Code Online (Sandbox Code Playgroud)

  • 不,出于安全原因,我更愿意分两步进行。合并和删除的源是不可逆的。还需要 n.st anwer 中的附加步骤(删除目录)。 (5认同)
  • 但它实际上并没有移动 - 如果涉及大文件,速度影响是巨大的。 (4认同)
  • `--remove-source-files` 的优点是只删除成功传输的文件,因此您可以使用 `find` 删除空目录,并且将保留所有未传输的内容,而无需检查 `rsync` l 输出。 (3认同)

pal*_*wim 89

您可以使用cp命令的-l选项,它会在同一文件系统上创建文件的硬链接,而不是完整的数据副本。以下命令将文件夹复制到父文件夹 ( ),该文件夹已包含名称为 的目录。source/folderdestinationfolder

cp -rl source/folder destination
rm -r source/folder
Run Code Online (Sandbox Code Playgroud)

根据您的需要,您可能还想使用-P( --no-dereference- 不要取消引用符号链接) 或-a( --archive- 保留所有元数据,还包括-P选项)。

  • 该解决方案的出色之处可能会被视为不是公认的答案。这是一个优雅的解决方案。你得到了`cp`的合并能力和`mv`的操作时间。 (13认同)
  • @rautamiekka:我想您是在问使用硬链接的原因。如果您不知道什么是硬链接以及为什么要使用它们,那么您可能不应该走这条路。但是,创建硬链接不会进行完整复制,因此此操作所需的时间比完整复制要少几个数量级。而且,您将使用硬链接而不是软链接,这样您就可以删除源文件并仍然拥有正确的数据,而不是指向无效路径的指针。和 `cp` 而不是 `rsync`,因为每个系统都有 `cp` 并且每个人都熟悉它。 (9认同)
  • 如果您知道不需要移动目标上已经存在的文件,您还想添加`-n` (4认同)
  • @Ruslan:这是真的,但是如果没有使用任何方法跨文件系统的副本,您就无法移动。甚至`mv /fs1/file /fs2/`(跨文件系统)也会执行复制然后删除。 (2认同)
  • 是的,但是尽管 `mv` 可以工作(假设目标目录尚不存在),即使不是“有效地”或您称之为什么,`cp -rl` 也会失败。 (2认同)

Jon*_*yer 26

我推荐这四个步骤:

cd ${SOURCE}; 
find . -type d -exec mkdir -p ${DEST}/\{} \; 
find . -type f -exec mv \{} ${DEST}/\{} \; 
find . -type d -empty -delete
Run Code Online (Sandbox Code Playgroud)

或者更好的是,这是一个实现类似于mv以下语义的脚本:

#!/bin/bash

DEST="${@:${#@}}"
ABS_DEST="$(cd "$(dirname "$DEST")"; pwd)/$(basename "$DEST")"

for SRC in "${@:1:$((${#@} -1))}"; do   (
    cd "$SRC";
    find . -type d -exec mkdir -p "${ABS_DEST}"/\{} \;
    find . -type f -exec mv \{} "${ABS_DEST}"/\{} \;
    find . -type d -empty -delete
) done
Run Code Online (Sandbox Code Playgroud)

SRC 和 DEST 变量周围的引号将保留路径名中的空格。


小智 16

这是一种合并目录的方法。它比 rsync 快得多,因为它只是重命名文件而不是复制它们然后删除它们。

cd source; find -type f -print0 | xargs -0 -n 1 -I {} mv '{}' 'dest/{}'
Run Code Online (Sandbox Code Playgroud)

  • 实际上,除了创建丢失的目录之外,Jewel 的代码完全符合用户的要求。也许你应该再看看? (18认同)
  • 我会添加在 find 中使用“-print0”和在 xargs 中使用“-0”,因为有些文件的名称中有空格。此外,还有一个小问题,如果名称包含括号,它们将不会被移动。 (3认同)
  • 对于少量文件,这比 rsync 快得多,但它为每个文件派生一个新进程,因此对于大量小文件,性能很糟糕。@palswim 的回答没有遇到这个问题。 (3认同)
  • 如果 `dest` 中已经是一个与 `source` 中同名的目录,则该命令将失败。这些文件将被移动到一个 `dest`,它在 `source` 中。该命令无非是 `mv source/* source/dest/.` (2认同)

Ale*_*inn 6

mv与 一起使用find您可以一次性完成此操作。

cd "$SRC"
find -type d -exec mkdir -vp "$DST"/{} \; -or -exec mv -nv {} "$DST"/{} \;
Run Code Online (Sandbox Code Playgroud)

... 其中$SRC$DST分别是源目录和目标目录。


解释

  • -type d测试项目是否为目录。如果是目录,我们继续下一步操作或测试: -exec …
  • 在 中-exec … {} \;{}被替换为当前项目的路径,相对于当前工作目录。的\;指示此的结束-exec …命令。
  • mkdir -pv …,-pv等价于-p -v。根据-p需要创建所有中间目录的方法,如果目录已经存在,则不会引发错误。的-v手段--verbose,只是告诉它打印消息为每个创建的目录,所以你可以看到它在做什么。 "$DST"/{}将扩展到目标目录,包括所有需要的引号。
  • -or是有趣的部分,它允许我们一次性完成。使用该find命令,每个测试(例如-type d)或动作(例如-exec …)都会导致真或假状态,具体取决于测试是通过还是动作成功。测试和动作可以使用连接-and-or-not-true-false,和\( … \)当您在没有显式布尔运算符的情况下添加多个测试和/或操作时,它们会被隐式地 AND 运算在一起。 因此,上述命令等同于这样的: find \( -type d -and -exec mkdir -vp "$DST"/{} \; \) -or -exec mv -nv {} "$DST"/{} \;。因此,如果-type d通过,则继续进行下一个动作 ( -exec …)。如果没有,那么第一个分支-or 是假的,它转到第二个分支,它涵盖了不是目录的任何东西(例如,文件)。
  • mv -nv {} "$DST"/{},-nv等价于-n -v。该-n告诉它不能在目标目录覆盖任何文件。该-v告诉它为每个文件移动到报告的消息,所以你可以看到它在做什么。
  • 将在移动文件之前创建目录。 默认find使用广度优先遍历。
  • {}不需要用引号括起来,即使它代表的项目包括空格。
  • 源上的空目录将保留。

例子

如果要将 /usr/local 复制到 /usr 中,可以这样输入。

cd /usr/local
find -type d -exec mkdir -vp ../{} \; -or -exec mv -nv {} ../{} \;
Run Code Online (Sandbox Code Playgroud)

它会导致这样的命令:

mkdir -pv .././bin
mv -nv ./bin/pip .././bin/pip
mv -nv ./bin/pip3 .././bin/pip3
mv -nv ./bin/python3 .././bin/python3
mv -nv ./bin/python3 .././bin/python3
mv -nv ./bin/xxhsum .././bin/xxhsum
mkdir -pv .././etc
mkdir -pv .././include
mv -nv ./include/xxh3.h .././include/xxh3.h
mv -nv ./include/xxhash.h .././include/xxhash.h
Run Code Online (Sandbox Code Playgroud)

… 等等

如何预览

要查看将运行哪些命令,请echo在每个命令之前添加,就在 之后-exec,如下所示:

mkdir -pv .././bin
mv -nv ./bin/pip .././bin/pip
mv -nv ./bin/pip3 .././bin/pip3
mv -nv ./bin/python3 .././bin/python3
mv -nv ./bin/python3 .././bin/python3
mv -nv ./bin/xxhsum .././bin/xxhsum
mkdir -pv .././etc
mkdir -pv .././include
mv -nv ./include/xxh3.h .././include/xxh3.h
mv -nv ./include/xxhash.h .././include/xxhash.h
Run Code Online (Sandbox Code Playgroud)


ash*_*shr 5

实现此目的的一种方法是使用:

mv folder/* directory/folder/
rmdir folder
Run Code Online (Sandbox Code Playgroud)

只要没有两个文件具有相同的名称folderdirectory/folder,你会得到相同的结果,即合并。

  • @JakeGould 一点也不。:) (5认同)
  • 请注意,这不适用于隐藏文件 (4认同)
  • `rm文件夹`究竟是如何工作的? (3认同)

gjs*_*gjs 5

对于最纯粹的副本,我使用 tar (-)B 块读取复制方法。

例如,从源路径中(如有必要,“cd”在那里):

tar cBf - <sourcefolder> | (cd /your/target/folder ; tar xBf -)
Run Code Online (Sandbox Code Playgroud)

这将创建源树的精确副本,所有者和权限完好无损。如果目标文件夹存在,数据将被合并。只有已经存在的文件才会被覆盖。

例子:

 $ cd /data1/home
 $ tar cBf - jdoe | (cd /data2/home ; tar xBf -)
Run Code Online (Sandbox Code Playgroud)

复制操作成功后,您可以删除源 ( rm -rf <source>)。当然,这不是一个精确的移动:数据将被复制,直到您删除源。

作为选项,您可以详细(在屏幕上显示正在复制的文件),使用 -v: tar cBvf -

  • c: 创建
  • B: 读取完整块(用于管道读取)
  • v: 冗长
  • f: 要写入的文件
  • x: 提炼
  • -:标准输出/标准输入

sourcefolder也可以*(对于当前文件夹中的任何内容)

  • @muru 并非总是如此。在编写脚本时指定 `-f -` 是最安全的。 (2认同)