题
在 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
解决方案
在bash中:
find /foo -iname '*.txt' -exec cp \{\} /dest/ \;
find
会找到该路径下的所有文件 /foo
匹配通配符 *.txt
, ,不区分大小写(这就是 -iname
方法)。对于每个文件, find
将执行 cp {} /dest/
, ,用找到的文件代替 {}
.
其他提示
Magnus 解决方案的唯一问题是它为每个文件分叉出一个新的“cp”进程,这并不是非常有效,尤其是在存在大量文件的情况下。
在 Linux(或其他具有 GNU coreutils 的系统)上,您可以执行以下操作:
find . -name "*.xml" -print0 | xargs -0 echo cp -t a
(当你的文件名中包含奇怪的字符(例如空格)时,-0 允许它工作。)
不幸的是,我认为 Mac 带有 BSD 风格的工具。有人知道相当于“-t”开关的“标准”吗?
上面的答案不允许名称冲突,因为询问者不介意文件被覆盖。
我确实介意文件被覆盖,所以想出了一种不同的方法。将路径中的每个 / 替换为 - 保留名称中的层次结构,并将所有文件放入一个平面文件夹中。
我们使用 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 的目录。
如果您确实只想运行一个命令,为什么不使用 cons 命令并运行它呢?就像这样:
$ 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 选项需要记住的语法更少。
就 FreeBSD 机器上 cp 的手册页而言,不需要 -t 开关。如果传递两个以上的名称,cp 将假定命令行上的最后一个参数为目标目录。