Если 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
команды s@^@/@
, s/$/$/
и s/\([\.[\*]\|\]\)/\\\1/g
включеныкаждая строка filelist.txt
и распечатывает их.Эти команды преобразуют имена файлов в формат, который будет лучше работать с 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
.