mv
将名为“文件夹”的文件夹移动到已经包含“文件夹”的目录中,它们将合并还是将被替换?#1 楼
mv
无法合并或覆盖目录,即使使用--force
选项,也会失败并显示消息“ mv:无法将'a'移至'b':目录不为空”。您可以使用其他工具(例如
rsync
,find
甚至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
的操作才能摆脱空的源目录树。#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
只要在
folder
和directory/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 楼
将mv
与find
一起使用。您可以一次完成此操作。 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 {} ../{} \;
pre >
将导致如下所示的命令:
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
评论
听起来您只尝试了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