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

Unix Shell文件复制展平文件夹结构

  •  38
  • Sergio Acosta  · 技术社区  · 16 年前

    在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 回复  |  直到 16 年前
        1
  •  52
  •   Magnus Hoff    16 年前

    在巴什:

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

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

        2
  •  13
  •   Stephen Darlington    16 年前

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

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

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

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

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

        3
  •  11
  •   Rob Styles    14 年前

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

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

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

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

    其中,/from和./to是从和到mv的目录。

        4
  •  3
  •   Jon Ericson Homunculus Reticulli    16 年前

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

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

    但是,如果您不经常这样做或者拥有大量的文件,那么从性能上讲,这就不重要了。不过,要小心名称合谋。我在测试中注意到GNUCP至少会警告碰撞:

    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    16 年前

    至于一个freebsd盒子上的cp的手册页,不需要-t开关。如果传递的名称超过两个,则cp将假定命令行上的最后一个参数为目标目录。