【问题标题】:Iteratively copy a directory's contents to multiple other directories in OS X Terminal (bash)迭代地将目录的内容复制到 OS X 终端 (bash) 中的多个其他目录
【发布时间】:2012-02-24 20:33:54
【问题描述】:

我有一个目录(比如说dir1),其中每个子目录都包含相同的内容。像这样:

dir1
  sub1
    subsub1
    file1
    file2
    subsub2
    [etc.]
  sub2
    [same as in sub1]
  sub3
    [...]
  aberration

所以,你看,还有一个不同的子目录。

然后,我有另一个目录 (dir2),其中包含我想用来替换每个 sub1sub2sub3 等的内容,但是 不是 aberration。我想覆盖目标中的任何文件和目录(dir2/subsub1 将替换整个dir1/sub5/subsub1 目录,无论其内容如何)。 [编辑:我意识到我在前一句中将 dir1 和 dir2 颠倒了。我已经对其进行了编辑以更正错误。]

我可以运行一个命令来完成此操作吗?我用xargsrsyncfor 循环查看了cp,但我的大脑已经扭曲了。

我也很乐意把它写成一个 shell 脚本,但我还是迷路了。

【问题讨论】:

    标签: bash unix terminal


    【解决方案1】:

    您可以使用 rsync 执行此操作,例如:

    rsync -av --exclude abberation dir1/ dir2
    

    dir1/ 的尾部斜杠很重要,因为它会导致 rsync 复制 dir1 的内容,而不是目录本身。如果要删除不在原始树中的文件,请使用--delete,否则,它们将被保留。

    【讨论】:

    • 我写的内容可能不够清楚,因为我认为这会将dir1的内容替换为dir2的内容,对吧?但我想做的是用dir2的内容替换每个subnum的内容,所以我需要以某种方式循环遍历每个或类似的东西。
    • 另外,我意识到我在上面的一句话中颠倒了 dir1 和 dir2。我已经纠正了错误(见编辑:原文)。很抱歉造成混乱。
    • 让我们使用rsync -av --exclude abberation src/ dest。这会将src的所有文件和子目录复制到dest
    • 好的,谢谢,但这仍然不会遍历我的许多子文件夹,将每个子文件夹用作新的 dest,对吗?
    【解决方案2】:

    表面上:

    cd /path/to/dir1
    cp -rf sub* /path/to/dir2
    

    作为一种技术,它依赖于能够通过 glob 模式 sub* 区分 aberrationsub1 等。调整它以适应您的环境的细节。如果最坏的情况出现在最坏的情况下,请创建一个包含非异常目录名称列表的文件:

    cp -rf $(<non-aberrational-names) /path/to/dir2
    

    这不会删除在 dir2/subN 下找到但不在 dir1/subN 中的文件。为此,您可能会添加:

    cd /path/to/dir2 &&
    rm -fr sub*
    cd /path/to/dir1
    cp -fr sub* /path/to/dir2
    

    小心 - rm -fr 很危险。


    在评论中,我指出:

    如果您重视自己的理智,请使用一致的名称。如果您被其他人的精神错乱所困扰,那么您必须指定/path/to/dir1/path/to/dir2 下的目录之间的映射,可能是通过一个文件(我们希望路径名不包含空格) !)。然后你可以这样做:while read src dst; do cp -fr $src $dst; done &lt; directory-mapping-file

    回复的是:

    啊,是的,我的理智。恐怕已经大部分消失了,这些子目录是客户名称。我的决定,但是一个我现在无法真正改变的旧决定。所以这似乎对我来说可以很好地使用映射文件。但如果你不介意,你能告诉我文件是什么样子的吗?

    啊,是的,我很熟悉发现我在很久以前的时候并不完全清醒,我不得不忍受后果。

    我想到的方案,假设路径名不包含空格,非常简单:

    目录映射文件

    /path/to/dir1/sub1    /path/to/dir2/subsub1
    /path/to/dir1/sub2    /paht/to/dir2/semisub-2
    

    即包含dir1下的子目录名称和dir2下的名称。

    目录复制代码

    然后你使用它的方式类似于这个(小心地做模糊适当的错误检查):

    while read src tgt
    do
        [ ! -d "$src" ] && { echo "$0: Not a directory: $src" 1>&2; continue; }
        [ ! -d "$tgt" ] && { echo "$0: Not a directory: $tgt" 1>&2; continue; }
        echo "Mapping files under $src to $tgt"
        (
        if cd "$src"
        then cp -rf . "$tgt"
        else echo "$0: Oops! cd $src failed" 1>&2
        fi
        )
    done < /path/to/dir1/directory-mapping-file
    

    显然,您可以在任何方便的地方找到目录映射文件。请注意,除非您小心(如图所示)使用正确的相对名称,否则您最终可能会在目标目录下出现不需要的目录层次结构。绝对要试验并仔细检查脚本是否为您提供所需的内容。另请注意,cd 命令是在显式子 shell 中完成的(( ... ) 表示法。这将目录更改隔离到子 shell,这通常比在父 shell 中执行 cd 更有弹性. 如果来自cd 命令的错误消息足够,则不需要else 子句,您可以将其全部简化为:

    cd "$src" && cp -rf . "$tgt"
    

    请注意,我引用了这些名称;这可能是老式的 shell 脚本,但是如果您曾经更改为路径名包含空格的系统,如果您始终将名称用引号括起来,则更容易让脚本与各种 shell 一起正常工作。

    一个很好的改进是允许文件中的 cmets 和空行:

    sed -e 's/#.*//' -e '/^[ \t]*$/d' /path/to/dir1/directory-mapping-file |
    while read src tgt
    do
        ...as above...
    done
    

    请注意,\t 可能需要是一个实际的选项卡(但在 Markdown 中显示选项卡有点困难)。我有一个脚本,我称之为 nbncl(非空白、非注释行),它封装了所示 sed 命令的逻辑(尽管它实际上是一个非最小的 Perl 脚本)。使用它给出:

    nbncl /path/to/dir1/directory-mapping-file |
    while read src tgt
    ...as before...
    

    【讨论】:

    • 谢谢,这很有帮助,但我的子目录彼此之间的名称不同(它们每个都有唯一的名称)。另外,我不想删除整个 sub* 目录。我只想将新文件复制到其中,覆盖任何预先存在的重复项。
    • 如果您重视自己的理智,请使用一致的名称。如果您被其他人的精神错乱所困扰,那么您必须指定/path/to/dir1/path/to/dir2 下的目录之间的映射,可能是通过一个文件(我们希望路径名不包含空格) !)。然后你可以这样做:while read src dst; do cp -fr $src $dst; done &lt; directory-mapping-file.
    • 啊,是的,我的理智。恐怕已经大部分消失了,这些子目录是客户名称。我的决定,但是一个我现在无法真正改变的旧决定。所以这似乎对我来说可以很好地使用映射文件。但如果你不介意,你能告诉我文件是什么样的吗?
    • 哇,一流的答案,乔纳森,谢谢!等我全部设置好后我会再次发表评论。
    猜你喜欢
    • 1970-01-01
    • 2015-10-15
    • 2012-07-24
    • 1970-01-01
    • 2010-11-21
    • 1970-01-01
    • 2011-09-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多