У меня есть каталог (например, abc/def/efg
) со многими подкаталогами (например: abc/def/efg/(1..300)
). Все эти подкаталоги имеют общий файл (например, file.txt
). Я хочу искать строку только в этом file.txt
исключая другие файлы. Как я могу сделать это?
Я использовал grep -arin "pattern" *
, но это очень медленно, если у нас есть много подкаталогов и файлов.
В родительском каталоге Вы могли использовать find
и затем работать grep
только на тех файлах:
find . -type f -iname "file.txt" -exec grep -Hi "pattern" '{}' +
Вам не нужно 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).
Создание 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 прочь.
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' {} +
Метод дан в ответе 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" {} \;
Только, чтобы указать, что, если условия вопроса могут быть взяты литературные, можно использовать прямой grep:
grep 'pattern' abc/def/efg/*/file.txt
или
grep 'pattern' abc/def/efg/{1..300}/file.txt