代码之家  ›  专栏  ›  技术社区  ›  Sergio Acosta

Unix shell文件复制扁平化文件夹结构

  •  44
  • Sergio Acosta  · 技术社区  · 17 年前

    在UNIX bash shell(特别是Mac OS X Leopard)上,将具有特定扩展名的每个文件从文件夹层次结构(包括子目录)复制到同一目标文件夹(没有子文件夹)的最简单方法是什么?

    显然,源层次结构中存在重复的问题。我不介意它们被覆盖。

    示例:我需要复制以下层次结构中的每个.txt文件

    /foo/a.txt
    /foo/x.jpg
    /foo/bar/a.txt
    /foo/bar/c.jpg
    /foo/bar/b.txt
    

    转到名为“dest”的文件夹并获取:

    /dest/a.txt
    /dest/b.txt
    
    5 回复  |  直到 17 年前
        1
  •  52
  •   Magnus Hoff    17 年前

    在bash中:

    find /foo -iname '*.txt' -exec cp \{\} /dest/ \;
    

    find 将找到路径下的所有文件 /foo 匹配通配符 *.txt ,对案件不敏感(就是这样 -iname 手段)。对于每个文件, 找到 将执行 cp {} /dest/ ,用找到的文件代替 {} .

        2
  •  13
  •   Stephen Darlington    17 年前

    Magnus解决方案的唯一问题是,它为每个文件分叉一个新的“cp”进程,这不是非常有效,特别是在有大量文件的情况下。

    在Linux(或其他使用GNU coreutils的系统)上,您可以执行以下操作:

    find . -name "*.xml" -print0 | xargs -0 echo cp -t a
    

    (当文件名中有奇怪的字符(如空格)时,-0允许它工作。)

    不幸的是,我认为Mac附带了BSD风格的工具。有人知道一个相当于“-t”开关的“标准”吗?

        3
  •  11
  •   Rob Styles    15 年前

    上面的答案不允许名称冲突,因为提问者不介意文件被重写。

    我确实介意文件被重写,所以想出了一种不同的方法。将路径中的每个/替换为-保持名称中的层次结构,并将所有文件放在一个平面文件夹中。

    我们使用find获取所有文件的列表,然后使用awk创建一个包含原始文件名和修改后的文件名的mv命令,然后将这些文件传递给bash执行。

    find ./from -type f | awk '{ str=$0; sub(/\.\//, "", str); gsub(/\//, "-", str); print "mv " $0 " ./to/" str }' | bash
    

    哪里。/从和。/to是mv from和to的目录。

        4
  •  3
  •   Jon Ericson Homunculus Reticulli    17 年前

    如果你真的只想运行一个命令,为什么不运行一个呢?像这样:

    $ find /foo  -name '*.txt' | xargs echo | sed -e 's/^/cp /' -e 's|$| /dest|' | bash -sx
    

    但这在性能方面并不重要,除非你经常这样做或有大量文件。不过,要小心名称勾结。我在测试中注意到GNU cp至少会警告冲突:

    cp: will not overwrite just-created `/dest/tubguide.tex' with `./texmf/tex/plain/tugboat/tubguide.tex'
    

    我认为最干净的是:

    $ find /foo  -name '*.txt' | xargs -i cp {} /dest
    

    要记住的语法比-exec选项少。

        5
  •  1
  •   agnul    17 年前

    就FreeBSD上cp的手册页而言,不需要-t开关。如果传递了两个以上的名称,cp将假设命令行上的最后一个参数是目标目录。