Оболочка: найти файлы в списке в каталоге - PullRequest
7 голосов
/ 31 марта 2012

У меня есть список, содержащий около 1000 имен файлов для поиска в каталоге и его подкаталогах.Существуют сотни подкаталогов с более чем 1 000 000 файлов.Следующая команда запустит find 1000 раз:

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

Есть ли намного более быстрый способ сделать это?

Ответы [ 4 ]

13 голосов
/ 31 марта 2012

Если 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.

3 голосов
/ 19 октября 2016

Если filelist.txt - простой список:

$ find /dir | grep -F -f filelist.txt

Если filelist.txt - список шаблонов:

$ find /dir | grep -f filelist.txt
3 голосов
/ 31 марта 2012

Использование xargs(1) для цикла while может быть немного быстрее, чем в bash.

Как это

xargs -a filelist.txt -I filename find /dir -name filename

Будьте осторожны, если имена файлов в filelist.txt содержат пробелы, прочитайте второй абзац в разделе ОПИСАНИЕ xargs(1) manpage об этой проблеме.

Улучшение, основанное на некоторых предположениях. Например, a.txt находится в filelist.txt, и вы можете убедиться, что в / dir есть только один a.txt. Затем вы можете указать find(1) выйти рано, когда он найдет экземпляр.

xargs -a filelist.txt -I filename find /dir -name filename -print -quit

Другое решение. Вы можете предварительно обработать файл listlist.txt, превратив его в список аргументов find(1), как этот. Это уменьшит find(1) вызовов:

find /dir -name 'a.txt' -or -name 'b.txt' -or -name 'c.txt'
0 голосов
/ 14 июня 2015

Я не совсем уверен в вопросе здесь, но я пришел на эту страницу после попытки найти способ обнаружить, какие 4 из 13000 файлов не удалось скопировать.

Ни один из ответов не сделал это для меня, поэтому я сделал это:

cp file-list file-list2
find dir/ >> file-list2
sort file-list2 | uniq -u

В результате был получен список из 4 необходимых мне файлов.

Идея состоит в том, чтобы объединить два списка файлов для определения уникальных записей. sort используется для создания дублирующихся записей рядом друг с другом, что является единственным способом uniq их отфильтровать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...