Рекурсивно ищите шаблон/текст только в указанном имени файла каталога?

У меня есть каталог (например, abc/def/efg) со многими подкаталогами (например: abc/def/efg/(1..300)). Все эти подкаталоги имеют общий файл (например, file.txt). Я хочу искать строку только в этом file.txt исключая другие файлы. Как я могу сделать это?

Я использовал grep -arin "pattern" *, но это очень медленно, если у нас есть много подкаталогов и файлов.

16
задан 3 January 2017 в 10:19

5 ответов

В родительском каталоге Вы могли использовать find и затем работать grep только на тех файлах:

find . -type f -iname "file.txt" -exec grep -Hi "pattern" '{}' +
21
ответ дан 23 November 2019 в 02:26

Вам не нужно find для этого; grep может обработать это превосходное самостоятельно:

grep "pattern" . -airn --include="file.txt"

От man grep :

--exclude=GLOB
      Skip  files  whose  base  name  matches  GLOB  (using   wildcard
      matching).   A  file-name  glob  can  use  *,  ?,  and [...]  as
      wildcards, and \ to quote  a  wildcard  or  backslash  character
      literally.

--exclude-from=FILE
      Skip  files  whose  base name matches any of the file-name globs
      read from FILE  (using  wildcard  matching  as  described  under
      --exclude).

--exclude-dir=DIR
      Exclude  directories  matching  the  pattern  DIR from recursive
      searches.

--include=GLOB
      Search  only  files whose base name matches GLOB (using wildcard
      matching as described under --exclude).
18
ответ дан 23 November 2019 в 02:26

Вы могли также использовать globstar.

Создание grep команды с find, как в ответе Zanna, очень устойчивый, универсальный, и портативный способ сделать это (см. также ответ sudodus). И muru отправил превосходный подход использования grep --include опция. Но если Вы хотите использовать просто grep команда и Ваша оболочка, существует другой способ сделать это - можно заставить саму оболочку выполнить необходимую рекурсию:

shopt -s globstar   # you can skip this if you already have globstar turned on
grep -H 'pattern' **/file.txt

-H флаг делает grep покажите имя файла, даже если только один файл соответствия найден. Можно передать -a, -i, и -n флаги (от Вашего примера) к grep также, если это - то, в чем Вы нуждаетесь. Но не передавайте -r или -R при использовании этого метода. Это - оболочка, которая рекурсивно вызывает каталоги в расширении шаблона шарика, содержащего **, и нет grep.

Эти инструкции характерны для оболочки Bash. Bash является пользовательской оболочкой по умолчанию в Ubuntu (и большая часть другого GNU/операционных систем Linux), поэтому если Вы находитесь на Ubuntu и не знаете, какова Ваша оболочка, это - почти наверняка Bash. Хотя популярные оболочки обычно поддерживают пересечение каталога ** шарики, они не всегда работают тот же путь. Для получения дополнительной информации см. превосходный ответ Stéphane Chazelas на результат ls *, ls ** и ls *** на Unix. SE.

Как это работает

Включение опции оболочки удара globstar делает ** пути соответствия, содержащие разделитель каталога (/). Это - таким образом рекурсивно вызывающий каталог шарик. А именно, как man bash объясняет:

Когда опция оболочки globstar включена, и * используется в контексте расширения пути, два смежных *s используемый, поскольку единственный шаблон будет соответствовать всем файлам и нулю или большему количеству каталогов и подкаталогов. Если сопровождается/, два смежных *s будут соответствовать только каталогам и подкаталогам.

Необходимо быть осторожными с этим, так как можно выполнить команды, которые изменяют или удаляют намного больше файлов, чем Вы предназначаете, особенно если Вы пишете ** когда Вы означали писать *. (Это безопасно в этой команде, которая не изменяет iles.) shopt -u globstar возвращает опцию оболочки globstar прочь.

Существует несколько практических различий между globstar и find.

find намного более универсально, чем globstar. Что-либо, что можно сделать с globstar, можно сделать с find команда также. Мне нравится globstar, и иногда это более удобно, но globstar не является общей альтернативой find.

Метод выше не смотрит в каталогах, имена которых запускаются с a .. Иногда Вы не хотите рекурсивно вызывать такие папки, но иногда Вы делаете.

Как с обычным шариком, оболочка создает список всех путей соответствия и передает их как аргументы Вашей команде (grep) вместо самого шарика. Если у Вас есть столько названных файлов file.txt то, что получающаяся команда была бы слишком длинной, чтобы система выполнилась, затем метод выше перестанет работать. На практике Вам были бы нужны (по крайней мере), тысячи таких файлов, но это могло произойти.

Методы то использование find не подвергаются этому ограничению, потому что:

  • Путем Zanna сборки и выполнения a grep команда с потенциально многими параметрами пути. Но если больше файлов найдено, чем можно перечислить в единственном пути, +- завершенный -exec действие выполняет команду с некоторыми путями, затем выполняет ее снова еще с некоторыми путями и т.д. В случае grepлуг для строки в нескольких файлах, это производит корректное поведение.

    Как globstar метод, покрытый здесь, это печатает все согласующие отрезки длинной линии с путями, предварительно ожидаемыми каждому.

  • путем sudodus выполнения grep отдельно для каждого file.txt найденный. Если существует много файлов, это могло бы быть медленнее, чем некоторые другие методы, но это работает.

    Тот метод находит файлы и печатает их пути, сопровождаемые согласующими отрезками длинной линии если таковые имеются. Это - другой выходной формат от формата, произведенного моим методом, Zanna и muru's.

Получение цвета с find

Одно из непосредственных преимуществ использования globstar, по умолчанию на Ubuntu, grep произведет цветной вывод. Но можно легко получить это с find, также.

Учетные записи пользователей в Ubuntu создаются с псевдонимом, который делает grep действительно выполненный grep --color=auto (выполненный alias grep видеть). Это - хорошая вещь, которая в значительной степени только расширены псевдонимы, когда Вы выпускаете их в интерактивном режиме, но это означает это, если Вы хотите find вызвать grep с --color флаг, необходимо будет записать это явно. Например:

find . -name file.txt -exec grep --color=auto -H 'pattern' {} +
24
ответ дан 23 November 2019 в 02:26

Метод дан в ответе muru выполнения grep с --include флаг для определения имени файла, часто лучший выбор. Однако это может также быть, покончили find.

Подход в этом ответе использование find работать grep отдельно для каждого файла, найденного, и печать, путь к каждому файлу точно однажды, выше согласующих отрезков длинной линии найден в каждом файле. (Методы, которые печатают путь перед каждым согласующим отрезком длинной линии, охвачены в других ответах.)


Можно изменить каталог на вершину дерева каталогов, где у Вас есть те файлы. Затем выполненный:

find . -name "file.txt" -type f -exec echo "##### {}:" \; -exec grep -i "pattern" {} \;

Это печатает путь (относительно текущего каталога, ., и включая само имя файла) каждого файла назван file.txt, сопровождаемый всеми согласующими отрезками длинной линии в файле. Это работает потому что {} заполнитель для найденного файла. Путь каждого файла установлен кроме его содержания, будучи снабженным префиксом #####, и печатается только однажды, перед согласующими отрезками длинной линии из того файла. (Files позвонили file.txt это не содержит соответствий, все еще распечатали их пути.) Вы могли бы найти этот вывод менее нарушенным, чем, что Вы получаете из методов, которые печатают путь в начале каждого согласующего отрезка длинной линии.

Используя find как это почти всегда будет быстрее, чем выполнение grep на каждом файле (grep -arin "pattern" *), потому что find поиски файлов с корректным именем и пропусками все другие файлы.

GNU использования Ubuntu находит, который всегда расширяется {} даже когда это появляется в большей строке, как ##### {}:. Если Вам нужна Ваша команда для работы с find в системах, которые не могли бы поддерживать это, или Вы предпочитаете использовать -exec действие только при необходимости, можно использовать:

find . -name "file.txt" -type f -printf '##### %p:\n' -exec grep -i "pattern" {} \;

Для создания вывода легче читать можно использовать escape-последовательности ANSI для получения окрашенными именами файлов. Это заставляет заголовок пути каждого файла выделиться лучше от согласующих отрезков длинной линии, которые печатаются под ним:

find . -name file.txt -printf $'\e[32m%p:\e[0m\n' -exec grep -i "pattern" {} \;

Это заставляет Вашу оболочку поворачивать управляющий код для зеленого в фактическую escape-последовательность, которая производит зеленый в терминале, и сделать то же самое с управляющим кодом для нормального цвета. Эти Escape передаются find, который использует их, когда это печатает имя файла. ($' ' цитата необходима здесь потому что find -printf действие не распознает \e для интерпретации управляющих кодов ANSI.)

Если Вы предпочитаете, Вы могли бы вместо этого использовать -exec с системой printf команда (который действительно поддерживает \e). Так другой способ сделать то же самое:

find . -name file.txt -exec printf '\e[32m%s:\e[0m\n' {} \; -exec grep -i "pattern" {} \;
8
ответ дан 23 November 2019 в 02:26

Только, чтобы указать, что, если условия вопроса могут быть взяты литературные, можно использовать прямой grep:

grep 'pattern' abc/def/efg/*/file.txt

или

grep 'pattern' abc/def/efg/{1..300}/file.txt
0
ответ дан 23 November 2019 в 02:26

Другие вопросы по тегам:

Похожие вопросы: