一尘不染

Shell:在目录下的列表中查找文件

linux

我有一个包含约1000个文件名的列表,可在目录及其子目录下进行搜索。有数百个具有超过1,000,000个文件的子目录。以下命令将运行find 1000次:

cat filelist.txt | while read f; do find /dir -name $f; done

有更快的方法吗?


阅读 254

收藏
2020-06-02

共1个答案

一尘不染

如果filelist.txt每行只有一个文件名:

find /dir | grep -f <(sed 's@^@/@; s/$/$/; s/\([\.[\*]\|\]\)/\\\1/g' filelist.txt)

(该-f选项意味着grep搜索给定文件中的所有模式。)

的说明<(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更好地兼容的格式。

  • s@^@/@表示/在每个文件名之前放置一个。(^在正则表达式中表示“行首”)
  • s/$/$/表示$在每个文件名的末尾放置a 。(第一个$表示“行尾”,第二个只是字面量$,然后由grep解释为“行尾”)。

这两个规则的结合意味着grep将只寻找像.../<filename>这样的匹配,因此a.txt不匹配./a.txt.backup./abba.txt

s/\([\.[\*]\|\]\)/\\\1/g``\在每次出现. [
]或之前放置一个*。Grep使用正则表达式,这些字符被认为是特殊字符,但我们希望它们是普通字符,因此我们需要对其进行转义(如果不对它们进行转义,则文件名a.txt将会与匹配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

2020-06-02