44

在 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
4

5 回答 5

61

在 bash 中:

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

find/foo将查找与通配符匹配的路径下的所有文件*.txt,不区分大小写(这就是-iname意思)。对于每个文件,find将执行cp {} /dest/,找到的文件代替{}.

于 2008-08-26T09:26:18.137 回答
14

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

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

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

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

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

于 2008-08-26T11:02:36.717 回答
12

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

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

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

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

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

于 2010-07-12T12:18:08.917 回答
3

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

$ 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 选项要记住的语法更少。

于 2008-09-12T09:07:57.163 回答
1

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

于 2008-09-12T08:44:06.160 回答