如何使用 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.htmlhtml_new/a/b.html 替换html/a/b2.html 被复制自 html_new/a/b2.htmlhtml/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中作为专用分支下的子目录

使用 tar 将最新的 .gz 文件提取到另一个目录中

如何将某个子文件夹模式移动到另一个文件夹,使用bash保持结构?

如何使用 PHP 将文件从一个目录复制到另一个目录?