在 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 将假定命令行上的最后一个参数为目标目录。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top