在bash脚本中使用可变扩展查找-exec


14

我正在尝试在bash脚本中运行类似于以下命令的命令。它应搜索的所有子文件夹,$sourcedir并将特定类型的所有文件复制到的根级别$targetdir

#!/bin/bash

# These are set as arguments to the script, not hard-coded
sourcedir="/path/to/sourcedir"
targetdir="/path/to/targetdir"

find "$sourcedir" -type f -name "*.type" -exec sh -c 'cp "$1" "$2/`basename "$1"`"' "{}" "$targetdir" \;

这似乎很接近,除了{}没有传递$2-exec sh -c ...

我希望这样做尽可能接近“正确的方式”,并允许对文件名中的特殊字符(特别是单引号字符)进行容忍。

编辑:我看到有人建议使用xargs或参数链。我的印象是,这仅适用于有限数量的参数。例如,如果我有成千上万个.jpg文件,我正试图将它们从许多画廊目录中复制到一个巨大的幻灯片目录中,则解决方案链接参数仍然有效吗?

编辑2:我的问题是我_-exec命令中的第一个选项sh之前缺少。对于任何对如何使find命令起作用感到好奇的人,添加_,一切都会好起来的:

find "$sourcedir" -type f -name "*.type" -exec sh -c 'cp "$1" "$2"' _ "{}" "$targetdir" \;

我下面接受了一个答案,因为它可以完成相同的任务,但是效率更高且更优雅。


4
这就是为什么要xargs创建它来自动处理具有限制的常规命令上的大量参数的原因。还需要考虑的是,大多数max参数限制已针对标准GNU utils进行了极大改进。您还将看到性能上的好处,避免了所有那些涉及数千个文件的进程分叉。
JM Becker

使用gnu-find和+而不是“;”,也可以使用find一次处理多个参数。然后保存通过-print0传递的复杂参数。
用户未知

@userunknown:我在您的回答下面对此进行回复。
JM Becker '02

@user未知好吧,我喜欢这段代码。它至少完全兼容POSIX,并且可以在机器上完全没有任何GNU东西的情况下运行。有时候您确实要这样做,尤其是在工作的服务器上。
语法错误

Answers:


6

您要将特定类型的文件复制到特定目录吗?这是最好的选择xargs,甚至不需要sh。这是一种更合适的方法,也应该更有效地运行。

find "$sourcedir" -type f -name "*.type" | xargs cp -t targetdir

如果您需要处理特殊的文件名,请NULL用作分隔符

find "$sourcedir" -type f -name "*.type" -print0 | xargs -0 cp -t "$targetdir"

1
对于第二种情况,不要忘记添加-print0find-0xargs
SiegeX

@ SiegeX,在我注意到您的评论之前已经在这样做了。
JM Becker

另外,NULL如果您使用,则没有必要'{}',当您不使用时,它很重要。除POSIX合规性外,两者之间的真正好处是性能。
JM Becker '02

6

您需要将{}参数作为参数传递给shell,然后遍历每个arg。

find "$sourcedir" -type f -name "*.type" -exec sh -c 'for f; do cp "$f" "$0"; done' "$targetdir" {} +

注意:此方法的工作方式是,第一个arg是外壳程序的名称,我们可以通过将名称作为传递来利用它$targetdir,然后$0在外壳程序脚本中使用特殊参数来访问该targetdir。


1
"$targetdir"没有在单引号内扩展。
enzotib'2

5

如果您不相信xargs教堂:

find "$sourcedir" -type f -name "*.mp3" -exec cp -t "$targetdir" {} +

说明:

cp -t a b c d 

将b,c和d复制到目标目录a。

-exec cmd {} +

一次调用一大堆文件,而不是一个接一个地调用该命令(如果使用 ";"而不是,的+)。这就是为什么必须将targetdir拉到最前面并将其明确标记为目标的原因。

这适用于gnu-find,可能不适用于find的其他实现。当然,它也依赖于-t -flag。

TechZilla当然是正确的,因为 sh不需要调用cp。

如果您不使用xargs,这在大多数情况下通常不需要与结合使用find,那么您会从学习-print0and -0标志中解脱出来。


1
显然,任何人可能更喜欢哪种方法是主观的。如此说来,确实有理由仍要使用xargs。最大的例子,如果找不到文件怎么办?我一直使用xargs代替常规for循环find,范围要小得多。另外,find+在使用GNU 时才支持该功能find,而POSIX中未定义。因此,尽管您应该随意偏爱find一个人,但它并没有xargs所做的一切。当您考虑使用GNU时,xargs您还会获得-P使用多核的功能。xargs无论如何,值得学习。
JM Becker '02

从主观性上来说,我的观点显然确实有所不同。另一方面,客观上,您的答案也是正确的。它是少数可用的最佳解决方案之一。
JM Becker '02

@TechZilla:我希望记住当find开始支持并行调用时重新访问该站点。:)在大多数情况下进行复制/移动时,磁盘速度将是限制因素,但是SSD可能会改变图像。没错,提供非GNU解决方案是一件好事。否则,查找解决方案客观上更短,更简单,并且仅使用两个过程。
用户未知

0
read -p "SOURCE: " sourcedir
read -p "TYPE: " type
read -p "TARGET: " targetdir
find -L $sourcedir -iname "*.$type" -exec cp -v {} $targetdir \;

3
考虑添加一些解决方案的说明。
HalosGhost 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.