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

#1 楼

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


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



rsync可以合并将一个目录的内容转移到另一个目录(理想情况下,请使用--remove-source-files 1选项安全删除仅成功传输的源文件,并根据需要使用通常的权限/所有权/时间保留选项-a
…但这是

当前首选选项:您可以结合使用rsync--link-dest=DIR选项(在可能的情况下创建硬链接而不是复制文件内容),并将--remove-source-files组合到获得与常规mv非常相似的语义。
为此,需要给--link-dest一个n到源目录的绝对路径(或从目标到源的相对路径)。
…但这是以意外方式使用--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的参考。

–沃伦·杨(Warren Young)
2014年5月3日19:29

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

–乔纳森·梅耶(Jonathan Mayer)
2014-09-13 18:53

@Keith注意--delete仅删除目标目录中源目录中不存在的文件。

–n.st
16年11月9日在18:18



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

– allo
19年4月18日在8:17

@allo --link-dest是个好主意!从手册页来看,它最初不是要在源目录上运行的,但是它可以在快速测试中完美地(非常高效)地工作。我现在会推荐它,但是如果您有任何关于并发症的反馈,我将不胜感激。

–n.st
19年4月18日在20:32

#2 楼

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


评论


是否会像n.st的注释一样删除源文件?

–多米尼克
2014年5月3日19:01

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

–法齐
2014年5月3日19:10

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

–n.st
2014年5月3日23:51

但是它实际上并没有移动-如果涉及大文件,那么速度影响是巨大的。

– Alex
16年3月15日在15:12

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

–法齐
16 Mar 15 '16 at 19:58

#3 楼

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

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


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

评论


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

–palswim
16-2-16在18:24

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

–TheHerk
16 Dec 9'在14:41

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

– ndemou
17年5月8日在22:32

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

–palswim
17年6月29日在19:37

是的,但是尽管mv可以工作(只要目标dir还不存在),即使它不是“有效地”或您所谓的名称,它也会失败,cp -rl将会失败。

–俄罗斯
17-6-29在19:47



#4 楼

我建议执行以下四个步骤:
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

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

评论


精氨酸是来源,目的

–舒斯
16年1月4日在14:32

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

– LarsH
16年6月29日在3:16

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

– LarsH
16-6-29在3:26



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

–rofer
17年9月30日在16:01

为了正确处理空格,这需要在$ {@:1:$((($ {#@} -1))}前后加上引号。

– DLAN
20-09-18在22:25

#5 楼

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

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


评论


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

– Shadur
2014年9月5日下午5:14

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

–乔纳森·梅耶(Jonathan Mayer)
2014年9月13日在18:44

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

– markuz
15年8月18日在16:33

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

–b0fh
16 Mar 1 '16 at 14:43

如果在dest中已经有一个与source中同名的目录,则该命令将失败。文件将被移动到源中的目的地。该命令仅执行mv source / * source / dest /。

–天花板
16年8月25日在13:38



#6 楼

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

mv folder/* directory/folder/
rmdir folder


只要在folderdirectory/folder中没有两个文件具有相同的名称,您将获得相同的结果,即合并。

评论


rm文件夹如何工作?

–Giacomo1968
2014年5月4日在4:28

@JakeGould一点也不。 :)

–n.st
2014年5月4日下午6:58

rm文件夹-fR总是对我有用

–章鱼
2015年12月10日,下午2:43

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

–b0fh
16 Mar 1 '16 at 14:20

#7 楼

对于最纯粹的副本,我使用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:提取物

-:stdout / stdin

sourcefolder也可以是*(用于当前文件夹中的所有内容)

评论


通常无需指定f-到tar-默认值为从stdin / write到stdout。

–大师
17年6月27日在8:11

@muru并非总是如此。编写脚本时,最安全的指定-f。

–roaima
19年11月18日在7:44

@roaima啊,是的,这是GNU的东西。当我不得不使用BSD tar时有点

–大师
19年11月18日在7:56

#8 楼

只能遍历源文件树一次的快速Python解决方案
由于找不到令人满意的既有解决方案,我决定制作一个快速Python脚本来实现它。
特别地,此方法非常有效因为它只会使源文件树自下而上遍历。
它还使您可以根据自己的喜好快速调整文件覆盖处理之类的内容。
用法:
move-merge-dirs src/ dest/

会移动src/*dest/src/的所有内容都将消失。
move-merge-dirs
#!/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-两个目录或从Windows命令中移动替换-
在Python 3.7,Ubuntu 18.04上进行了测试。

#9 楼

mvfind一起使用。您可以一次完成此操作。

 cd "$SRC"
find -type d -exec mkdir -vp "$DST"/{} \; -or -exec mv -nv {} "$DST"/{} \;
 

…其中$SRC$DST是源目录和目标目录,

说明


-type d测试项目是否为目录。如果它是目录,则继续执行下一个操作或测试:-exec …
-exec … {} \;中,将{}替换为相对于当前工作目录的当前项目的路径。 \;指示此-exec …命令的结尾。
mkdir -pv …中,-pv等效于-p -v-p的意思是根据需要创建所有中间目录,如果目录已经存在,则不会引发错误。 -v的意思是--verbose,只是告诉它为创建的每个目录打印一条消息,因此您可以看到它在做什么。 "$DST"/{}将扩展到目标目录,其中包括所有需要的引号。
-or是有趣的部分,它使我们可以一口气完成此操作。使用find命令,每个测试(例如-type d)或操作(例如-exec …)的状态为true或false,具体取决于测试通过还是操作成功。可以使用-and-or-not-true-false\( … \)连接测试和动作。当您添加多个没有显式布尔运算符的测试和/或操作时,它们将被隐式地与在一起。因此,以上命令等效于此:find \( -type d -and -exec mkdir -vp "$DST"/{} \; \) -or -exec mv -nv {} "$DST"/{} \;。因此,如果-type d通过,则继续进行下一个动作(-exec …)。如果不是,则-or的第一个分支为false,然后转到第二个分支,该分支涵盖了所有非目录内容(例如文件)。
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 {} ../{} \;
  
将导致如下所示的命令:
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

...等等
如何预览
要查看将要运行的命令,请在每个命令前添加echo ,就在-exec之后,就像这样:
 cd "$SRC"
find -type d -exec echo mkdir -vp "$DST"/{} \; -or -exec echo mv -nv {} "$DST"/{} \;
                   ‾‾‾‾                               ‾‾‾‾
 


评论


很难相信您实际上已经按照记录显示将/ usr / local合并到/ usr中,但是也许您进行了一次一次性安装:)

– Ald In
20/12/22在12:25

#10 楼

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

评论


OP询问使用mv时会发生什么。

–don_crissti
16-2-26在13:49

#11 楼

这是一个对我有用的脚本。我更喜欢mv而不是rsync,所以我使用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年6月7日下午5:15

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

–xer0x
2015年6月8日在22:53



#12 楼

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

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


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


mv:无法将'/ source / path / folder'
移至'/ target / destination / folder':目录不为空


如果目标文件夹为空,则上面的命令可以正常工作。

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

$ 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 = movecp = copyrm =删除

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


#13 楼

没有意义的复制文件夹为空-YMMV

#!/bin/bash

# usage source1 .. sourceN dest

length=$(($#-1))
sources=${@:1:$length}
DEST=$(readlink -f ${!#})
for SRC in $sources; do
    pushd "$SRC";
    # Only one scan - we only need folders with files
    find . -type f | while read FILE ; do
        DIRNAME=`dirname "$FILE"`
        # Create the lowest level directory at once
        if [ ! -d "$DEST/$DIRNAME" ] ; then
            mkdir -v "$DEST/$DIRNAME"
        fi
        mv -v "$FILE" "$DEST/$FILE"
    done
    # Remove the directories no longer needed
    find . -type -d | sort -r | xargs -i rmdir "{}"
    popd
done



没有多次执行
即使顺序查找目录后,mkdir -p仍被执行


#14 楼

您可以将a和b合并为:

shopt -s dotglob
mv a/* b


在mv之前:

.
├── a
│   ├── c
│   │   └── x
│   └── .x
└── b
    ├── y
    └── .y
在mv之后:
/>
.
├── a
└── b
    ├── c
    │   └── x
    ├── .x
    ├── y
    └── .y


dotglob允许您移动隐藏的点文件,例如.x

使用rmdir删除空目录。

评论


如果文件夹a包含子文件夹y,会发生什么情况?

– AdminBee
20年1月30日在8:16

它导致错误,并且没有任何变化。

–先生__芬利
20年1月30日在8:30