我有一个包含约1000个文件名的列表,可在目录及其子目录下进行搜索。有数百个具有超过1,000,000个文件的子目录。以下命令将运行find 1000次:
cat filelist.txt | while read f; do find /dir -name $f; done
有更快的方法吗?
如果filelist.txt每行只有一个文件名:
filelist.txt
find /dir | grep -f <(sed 's@^@/@; s/$/$/; s/\([\.[\*]\|\]\)/\\\1/g' filelist.txt)
(该-f选项意味着grep搜索给定文件中的所有模式。)
-f
的说明<(sed 's@^@/@; s/$/$/; s/\([\.[\*]\|\]\)/\\\1/g' filelist.txt):
<(sed 's@^@/@; s/$/$/; s/\([\.[\*]\|\]\)/\\\1/g' filelist.txt)
在<( ... )被称为过程subsitution,并且有点类似$( ... )。这种情况是等效的(但是使用流程替换更整洁,可能更快一些):
<( ... )
$( ... )
sed 's@^@/@; s/$/$/; s/\([\.[\*]\|\]\)/\\\1/g' filelist.txt > processed_filelist.txt find /dir | grep -f processed_filelist.txt
调用sed运行命令s@^@/@,s/$/$/并s/\([\.[\*]\|\]\)/\\\1/g在命令的每一行上将其filelist.txt打印出来。这些命令将文件名转换为与grep更好地兼容的格式。
sed
s@^@/@
s/$/$/
s/\([\.[\*]\|\]\)/\\\1/g
/
^
$
这两个规则的结合意味着grep将只寻找像.../<filename>这样的匹配,因此a.txt不匹配./a.txt.backup或./abba.txt。
.../<filename>
a.txt
./a.txt.backup
./abba.txt
s/\([\.[\*]\|\]\)/\\\1/g``\在每次出现. [ ]或之前放置一个*。Grep使用正则表达式,这些字符被认为是特殊字符,但我们希望它们是普通字符,因此我们需要对其进行转义(如果不对它们进行转义,则文件名a.txt将会与匹配abtxt)。
s/\([\.[\*]\|\]\)/\\\1/g``\
.
[
]
*
abtxt
举个例子:
$ cat filelist.txt file1.txt file2.txt blah[2012].txt blah[2011].txt lastfile $ sed 's@^@/@; s/$/$/; s/\([\.[\*]\|\]\)/\\\1/g' filelist.txt /file1\.txt$ /file2\.txt$ /blah\[2012\]\.txt$ /blah\[2011\]\.txt$ /lastfile$
然后,Grep在搜索的输出时会将输出的每一行用作模式find。
find