合并文件夹与MV?


149

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

Answers:


120

mv无法合并或覆盖目录,即使使用此选项,也会失败并显示消息“ mv:无法将'a'移至'b':目录不为空”--force


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

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

这些变通办法中的哪一个(如果有)合适,将在很大程度上取决于您的特定用例。
与往常一样,请在执行任何这些命令之前先进行思考,然后进行备份。


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


听起来您只是尝试了的一种实现mv。有了更广泛的真理,这个答案会更好。Linux,BSD和“真实” Unix,或POSIX或SUS的参考。
沃伦·杨

@WarrenYoung你说得对,我只尝试了mvDebian是用于执行-重点是在尝试,因为联机帮助中没有提到这种行为...
n.st

38
rsync的缺点是它实际上是复制数据,而不是仅仅更改硬链接,如果要处理大量数据,这可能会占用大量资源。
乔纳森·梅耶

7
@Keith请注意,--delete仅删除目标目录中源目录中不存在的文件。
n.st

1
@JonathanMayer rsync是几个与硬链接相关的功能。例如,您可以仅使用-H函数保留硬链接,也可以使用硬链接目标文件--link-dest。但是,在使用它们之前,请参见手册页。
allo

87
rsync -av /source/ /destination/
(after checking)
rm -rf /source/

是否会像n.st的注释一样删除源文件?
多米尼克2014年

3
不,出于安全考虑,我希望分两步进行。合并和删除的源是不可逆的。还需要在n.st anwer中添加步骤(以删除目录)。
fazie

3
--remove-source-files具有仅删除成功传输的文件的优点,因此您可以用于find删除空目录,并且无需检查rsyncs的输出即可保留所有未传输的文件。
2014年

4
但是它实际上并没有移动-如果涉及大文件,则速度影响是巨大的。
亚历克斯(Alex)

但是您实际上无法执行纯粹的移动和合并。
fazie

64

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

cp -rl source/folder destination
rm -r source/folder

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


7
@rautamiekka:我想您是在问使用硬链接的原因。如果您不知道什么是硬链接以及为什么要使用它们,那么您可能不应该采用这种方法。但是,创建硬链接不会完成完整复制,因此此操作将比完整复制花费几个数量级的时间。并且,您将使用硬链接而不是软链接,以便可以删除源文件,并且仍然具有正确的数据,而不是指向无效路径的指针。而cp不是rsync因为每个系统都有cp,每个人都有一个与之熟悉。
palswim '16

7
该解决方案的出色之处可能不是被接受的答案。这是一个优雅的解决方案。您将获得的合并能力以及cp的运行时间mv
TheHerk '16

2
如果您知道不需要移动目标上已经存在的文件,则还想添加-n
ndemou

1
@Ruslan:的确如此,但是如果没有使用任何方法在文件系统之间进行复制的副本,您将无法移动。甚至mv /fs1/file /fs2/(跨文件系统)将执行复制,然后删除。
palswim

2
是的,但是尽管mv可以“有效”(只要目标目录不存在)仍然有效,即使它不是“有效”或您所说的任何方法,cp -rl也将失败。
Ruslan

23

我建议执行以下四个步骤:

cd ${SOURCE}; 
find . -type d -exec mkdir -p ${DEST}/\{} \; 
find . -type f -exec mv \{} ${DEST}/\{} \; 
find . -type d -empty -delete

或更妙的是,这是一个实现类似于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

Args是来源,目的
schuess

这看起来很有用。我很想用它来清理硬盘。在将大量备份委托给此脚本之前,其他任何专家都可以对此发表评论吗?:-)
LarsH '16

顺便说一句,如果您想做的等效项rsync -u(仅更新时才更新),mv(至少在某些版本中)也可以-u选择。但是,在那种情况下,您可能要删除非空源目录以及空目录,以涵盖源树中文件不是较新的情况。@schuess:如果需要,似乎可以有多个SOURCE参数。
LarsH '16

1
这不能很好地处理空白。我尝试了一些带有空格的目录,最后得到了一系列无限的嵌套目录。
rofer

16

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

cd source; find -type f -print0 | xargs -0 -n 1 -I {} mv '{}' 'dest/{}'

这很有趣,但只是与主题模糊相关,甚至与用户的要求无关。
Shadur 2014年

17
实际上,Jewel的代码完全可以满足用户的要求,除了创建丢失的目录外。也许您应该再看一次?
乔纳森·梅耶

3
我会在find中添加“ -print0”,在xargs中添加“ -0”,因为名称中包含有空格的文件。此外,还有一个小问题,如果名称包含括号,则不会移动它们。
markuz

2
对于少量文件,这比rsync快得多,但是它为每个文件派生了一个新进程,因此,对于大量小文件而言,性能非常糟糕。@palswim的答案不会遇到此问题。
b0fh

2
如果in dest已经是与in中同名的目录,则该命令将失败source。文件将被移至dest中的source。该命令不超过罢了mv source/* source/dest/.
ceving

3

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

mv folder/* directory/folder/
rmdir folder

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


3
rm folder工作原理如何?
JakeGould 2014年

5
@JakeGould一点也不。:)
2014年

rm folder -fR总是为我工作
章鱼

2
请注意,这不适用于隐藏文件
b0fh

2

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

例如,从源路径中(如果需要,在“ cd”中):

tar cBf - <sourcefolder> | (cd /your/target/folder ; tar xBf -)

这将创建源树的精确副本,其中所有者和权限保持不变。并且如果目标文件夹存在,则数据将被合并。仅已存在的文件将被覆盖。

例:

 $ cd /data1/home
 $ tar cBf - jdoe | (cd /data2/home ; tar xBf -)

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

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

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

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


f -通常不需要指定tar-默认值是从stdin / write读取到stdout。
muru

1

这是一个对我有用的脚本。与rsync相比,我更喜欢mv,因此我使用Jewel和Jonathan Mayer的解决方案。

#!/bin/bash

# usage source1 .. sourceN dest

length=$(($#-1))
sources=${@:1:$length}
DEST=$(readlink -f ${!#})
for SRC in $sources; do
    pushd $SRC;
    find . -type d -exec mkdir -p ${DEST}/{} \;
    find . -type f -exec mv {} ${DEST}/{} \;
    find . -type d -empty -delete
    popd
done

请注意,此解决方案无法正确转义路径名。
user12439 2015年

@ user12439,如果您告诉我要修复的部分,我将更新解决方案。
xer0x

1

使用cp或rsync之类的命令不是一个好主意。对于大文件,将需要很长时间。mv更快,因为它只更新inode而不物理复制文件。更好的选择是使用操作系统的文件管理器。对于Opensuse,有一个名为Konquerer的文件管理器。它可以移动文件而无需实际复制它们。它具有Windows中的“剪切和粘贴”功能。只需选择目录A中的所有子目录。右键单击并“移入”目录B,其中可能包含具有相同名称的子目录。它将合并它们。您还可以选择是否要覆盖或重命名具有相同名称的文件。


1
OP询问mv使用时会发生什么情况。
don_crissti

0

Python解决方案

由于找不到满意的预先存在的解决方案,因此我决定制作一个快速的Python脚本来实现它。

特别地,该方法是有效的,因为它仅在自下而上时遍历源文件树。

它还将允许您根据自己的喜好快速调整文件覆盖处理之类的内容。

用法:

move-merge-dirs src/ dest/

将移动的所有内容src/*dest/src/会消失。

移动合并目录

#!/usr/bin/env python3

import argparse
import os

def move_merge_dirs(source_root, dest_root):
    for path, dirs, files in os.walk(source_root, topdown=False):
        dest_dir = os.path.join(
            dest_root,
            os.path.relpath(path, source_root)
        )
        if not os.path.exists(dest_dir):
            os.makedirs(dest_dir)
        for filename in files:
            os.rename(
                os.path.join(path, filename),
                os.path.join(dest_dir, filename)
            )
        for dirname in dirs:
            os.rmdir(os.path.join(path, dirname))
    os.rmdir(source_root)

if __name__ == '__main__':
    parser = argparse.ArgumentParser(
        description='Move merge src/* into dest. Overwrite existing files.'
    )
    parser.add_argument('src_dir')
    parser.add_argument('dest_dir')
    args = parser.parse_args()
    move_merge_dirs(args.src_dir, args.dest_dir)

GitHub上游

另请参阅:https : //stackoverflow.com/questions/22588225/how-do-you-merge-two-directories-or-move-with-replace-from-the-windows-command


0

这是用于将文件和文件夹移动到其他目标的命令:

$ mv /source/path/folder /target/destination/

切记mv如果该文件夹是b̲e̲i̲n̲g m̲e̲r̲ge̲d̲(即,目的地中已经存在另一个具有相同名称的文件夹)并且d̲e̲s̲t̲i̲n̲a̲t̲i̲o̲n̲ o̲n̲e̲ i̲s̲n̲o̲t̲ e̲m̲pt̲y,则该命令将不起作用。

mv:无法将“ /源/路径/文件夹”移动到“ /目标/目标/文件夹”:目录不为空

如果目标文件夹为空,则以上命令将正常运行。

因此,为了在任何情况下都可以合并两个文件夹,
请使用以下两个命令之一:

$ cp -rf /source/path/folder /target/destination/
$ rm -rf /source/path/folder

或将两者结合为一次性命令:

$ cp -rf /source/path/folder /target/destination/ && rm -rf /source/path/folder

mv =移动
cp =复制
rm =删除

-r用于目录(文件夹)
-f强制执行

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.