如何使用 Bash 将一个目录合并到另一个目录?
Posted
技术标签:
【中文标题】如何使用 Bash 将一个目录合并到另一个目录?【英文标题】:How do I merge one directory into another using Bash? 【发布时间】:2011-06-02 02:45:55 【问题描述】:我正在寻找将文件从一个目录合并到另一个目录的 shell 脚本。
示例:
html/
a/
b.html
index.html
html_new/
a/
b2.html
b.html
用法:
./mergedirs.sh html html_new
结果:
html/
a/
b.html
b2.html
index.html
html/a/b.html
被 html_new/a/b.html
替换html/a/b2.html
被复制自 html_new/a/b2.html
html/index.html
保持不变
【问题讨论】:
试过cp -r html_new/* html
?之后您可以删除html_new
目录。
【参考方案1】:
cp -r
不工作吗?
cp -r html_new/* html
或(因为第一个版本不会复制“.something”文件)
cd html_new; cp -r . ../html
如果复制目录中的任何文件是管道,请注意-r
从管道中读取。为避免这种情况,请改用-R
。
【讨论】:
当“合并”多个目录等时,还可以考虑使用-n
(禁止破坏)或-f
(强制)选项来保留现有文件或覆盖它们。【参考方案2】:
cd html
cp -r . /path/to/html_new
【讨论】:
【参考方案3】:你可能只想要cp -R $1/* $2/
——这是一个递归副本。
(如果可能存在隐藏文件(名称以点开头的文件),则应在该命令前加上 shopt -s dotglob;
以确保它们匹配。)
【讨论】:
【参考方案4】:看看rsync
rsync --recursive html/ html_new/
Rsync 需要设置很多标志,因此请查看 rsync 手册页了解详细信息
【讨论】:
【参考方案5】:cp -RT source/ destination/
source
中的所有文件和目录都将在 destination
中结束。例如,source/file1
将被复制到 destination/file1
。
-T
标志阻止 source/file1
被复制到 destination/source/file1
。 (很遗憾,macOS 上的 cp
不支持 -T
标志。)
【讨论】:
效果很好,它将 /folder/data 复制并合并到 folder2/data 我更喜欢这个解决方案,因为它可以“按原样”使用 .dotfiles。 在 Mac OS X / darwin 上,您需要使用cp -r source/ destination
-- 很棘手,您必须完全按照指示使用 /。
自豪地成为支持此答案的人,因此现在使其等于当前接受的答案?。
请注意,这将覆盖目标中与源同名的所有文件【参考方案6】:
只需使用 rsync - 除了远程复制之外,它还是用于本地文件复制和合并的绝佳工具。
rsync -av /path/to/source_folder/ /path/to/destination_folder/
请注意,源文件夹的尾部斜杠对于仅将 source_folder 的内容复制到目标文件夹是必需的。如果您不使用它,它将复制 source_folder 和它的内容,这可能不是您想要的,因为您想合并文件夹。
【讨论】:
有一个小错字,多余的连字符,所以应该是rsync -av /path/to/source_folder/ /path/to/destination_folder/
【参考方案7】:
尽管这个问题及其公认的答案很古老,但我还是要添加我的答案,因为目前使用 cp
的问题要么不处理某些极端情况,要么需要交互工作。通常,边缘案例/可脚本性/可移植性/多源并不重要,在这种情况下,简单性会胜出,最好直接使用cp
并减少标志(如其他答案)以减少认知负荷 - 但对于那些 other 次(或对于强大的可重用函数)这个调用/函数很有用,顺便说一句,它不是特定于 bash 的(我意识到这个问题是关于 bash 的,所以这只是一个奖励案子)。一些标志可以缩写(例如-a
),但为了解释起见,我已经明确地将所有标志都包含在长格式中(-R
除外,见下文)。显然,如果有某些您特别不想要的功能(或者您使用的是非 posix 操作系统,或者您的 cp
版本不处理该标志 - 我测试过),显然只需删除任何标志这个在 GNU coreutils 8.25 的 cp
):
mergedirs()
_retval=0
_dest="$1"
shift
yes | \
for _src do
cp -R --no-dereference --preserve=all --force --one-file-system \
--no-target-directory "$_src/" "$_dest" || _retval=1; break;
done 2>/dev/null
return $_retval
mergedirs destination source-1 [source-2 source-3 ...]
解释:
-R
:在某些系统上(特别是关于源目录中的特殊文件)与-r
/--recursive
的语义略有不同,如this answer 中所述
--no-dereference
:永远不要关注 SOURCE 中的符号链接
--preserve=all
:保留指定的属性(默认:模式、所有权、时间戳),如果可能的话,附加属性:上下文、链接、xattr、全部
--force
: 如果无法打开现有的目标文件,请将其删除并重试
--one-file-system
: 留在这个文件系统上
--no-target-directory
:将DEST视为普通文件(在this answer中解释,即:If you do a recursive copy and the source is a directory, then cp -T copies the content of the source into the destination, rather than copying the source itself.
)
[来自yes
的管道输入]:即使使用--force
,在这种特定的递归模式下,cp
仍然会在破坏每个文件之前询问,因此我们通过管道输出从yes
到它来实现非交互性李>
[管道输出到/dev/null
]:这是为了消除cp: overwrite 'xx'?
中的一连串问题
[return-val & early exit]:这样可以确保一旦复制失败就退出循环,如果有错误则返回1
顺便说一句:
一个时髦的新标志,我也在我的系统上使用它是--reflink=auto
,用于执行所谓的“轻拷贝”(写时复制,具有与硬链接相同的速度优势,以及相同的大小收益直到和与未来文件分歧的程度成反比)。这个标志在最近的 GNU cp
中被接受,并且不仅仅是在最近的 Linux 内核上兼容文件系统的无操作。 YMWV-a-lot 在其他系统上。
【讨论】:
【参考方案8】:cp --recursive --no-target-directory [--no-clobber] "[IN]" "[OUT]"
--no-target-directory
:将 OUT 视为普通文件,因此包含以 dot 开头的文件(隐藏文件)。
--no-clobber
:如果您不想覆盖现有文件。无论如何,内部目录永远不会被覆盖。
【讨论】:
以上是关于如何使用 Bash 将一个目录合并到另一个目录?的主要内容,如果未能解决你的问题,请参考以下文章
批处理文件合并另一个批处理,将目录中的修改文件复制到另一个目录
递归(许多子目录)查找pdf文件并合并为一个pdf文件(linux,bash)
markdown 将来自repo的分支合并到另一个repo中作为专用分支下的子目录