我有一个包含大约1000个文件名的列表,需要在一个目录及其子目录下进行搜索。有数百个子目录,其中有超过100万个文件。以下命令将运行1000次查找:
cat filelist.txt | while read f; do find /dir -name $f; done
有没有更快的方法来做到这一点?
filelist.txt
每行只有一个文件名:find /dir | grep -f <(sed 's@^@/@; s/$/$/; s/\([\.[\*]\|\]\)/\\\1/g' filelist.txt)
(-f
选项意味着grep会在给定文件中搜索所有模式。)
解释<(sed 's@^@/@; s/$/$/; s/\([\.[\*]\|\]\)/\\\1/g' filelist.txt)
:
<( ... )
是称为进程替代,与$( ... )
有些相似。这种情况等同于(但使用进程替换更整洁,也可能更快):
sed 's@^@/@; s/$/$/; s/\([\.[\*]\|\]\)/\\\1/g' filelist.txt > processed_filelist.txt
find /dir | grep -f processed_filelist.txt
调用sed
命令对filelist.txt
文件的每一行运行s@^@/@
、s/$/$/
和s/\([\.[\*]\|\]\)/\\\1/g
三个命令,并将其输出。这些命令将文件名转换为更适合用于grep的格式。
s@^@/@
意味着在每个文件名前加上/
。(^
在正则表达式中表示“行首”)s/$/$/
意味着在每个文件名后加上$
。(第一个$
表示“行末”,第二个是字面上的$
,然后由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
命令的输出中的每一行作为搜索模式。
如果 filelist.txt
是一个纯文本列表:
$ find /dir | grep -F -f filelist.txt
如果 filelist.txt
是一个模式列表:
$ find /dir | grep -f filelist.txt
使用xargs(1)
代替bash中的while循环可以更快地执行。
像这样:
xargs -a filelist.txt -I filename find /dir -name filename
xargs(1)
手册中“DESCRIPTION”部分的第二段关于此问题的说明。基于一些假设,可以进行改进。例如,a.txt在filelist.txt中,并且您可以确保在/dir中只有一个a.txt实例。然后,您可以告诉find(1)
在找到该实例时提前退出。xargs -a filelist.txt -I filename find /dir -name filename -print -quit
另一种解决方案是,您可以预处理filelist.txt,将其制成类似于find(1)
参数列表的形式。这将减少find(1)
的调用次数:
find /dir -name 'a.txt' -or -name 'b.txt' -or -name 'c.txt'
xargs -I
将被调用1000次。我刚在CentOS 7中测试了这个命令。 -I
开关将 xargs
切换到每行输入模式。从手册中可以看到:
未引用的空格不会终止输入项;相反,分隔符是换行符。意味着-x和-L 1。
在BSD(MacOS)中,-J
可以正确地打包命令行,但GNU xargs
不支持 -J
。 - Dima Korobskiy我不太确定这个问题的意思,但是我来到这个页面是为了寻找一种方法来发现哪4个文件在13000个文件中未能成功复制。
两个答案都没有解决我的问题,所以我做了以下操作:
cp file-list file-list2
find dir/ >> file-list2
sort file-list2 | uniq -u
这导致了我需要的4个文件列表。
思路是将两个文件列表合并以确定唯一条目。sort
用于使重复条目相邻,这是uniq
过滤它们的唯一方法。
grep -F -f FILE
。 - Jo So