У меня есть каталог (например, abc/def/efg
) со многими подкаталогами (например,:) abc/def/efg/(1..300)
. Все эти подкаталоги имеют общий файл (например, file.txt
). Я хочу искать строку только в этом, file.txt
за исключением других файлов. Как я могу это сделать?
Я использовал grep -arin "pattern" *
, но это очень медленно, если у нас много подкаталогов и файлов.
command-line
grep
find
Раджеш Келадиматх
источник
источник
Ответы:
В родительском каталоге вы можете использовать
find
и запускатьgrep
только эти файлы:источник
-H
кgrep
тому, чтобы в случаях, когда ему был передан только один путь, этот путь все еще печатался (а не только совпадающие строки из файла).Вы также можете использовать Globstar.
Создание
grep
команд с помощьюfind
, как в ответе Занны , является очень надежным, универсальным и переносимым способом сделать это (см. Также ответ Судодуса ). И Муру опубликовали отличный подход использованияgrep
«s--include
варианта . Но если вы хотите использовать толькоgrep
команду и вашу оболочку, есть другой способ сделать это - вы можете заставить саму оболочку выполнить необходимую рекурсию :В
-H
флаг маркиgrep
показать имя файла , даже если только один соответствующий файл найден. Вы можете передать-a
,-i
и-n
флаги (из вашего примера),grep
а также, если это то, что вам нужно. Но не пропустите-r
или-R
при использовании этого метода. Это оболочка, которая рекурсивно использует каталоги в расширении шаблона glob, содержащего**
, а неgrep
.Эти инструкции относятся к оболочке Bash. Bash - это пользовательская оболочка по умолчанию в Ubuntu (и в большинстве других операционных систем GNU / Linux), поэтому, если вы используете Ubuntu и не знаете, какая у вас оболочка, это почти наверняка Bash. Несмотря на то, что популярные оболочки обычно поддерживают
**
глобальные списки, они не всегда работают одинаково. Для получения дополнительной информации см Stéphane Chazelas «s отличный ответ на Результат логинсервера *, ** Ls и Ls *** на Unix.SE .Как это устроено
Включение опции оболочки globstar bash позволяет
**
сопоставлять пути, содержащие разделитель каталогов (/
). Таким образом, это рекурсивный глобус каталога. В частности, какman bash
объясняет:Вы должны быть осторожны с этим, так как вы можете запускать команды, которые изменяют или удаляют гораздо больше файлов, чем вы предполагаете, особенно если вы пишете,
**
когда намеревались писать*
. (Это безопасно в этой команде, которая не меняет никаких файлов.)shopt -u globstar
Отключает опцию оболочки globstar.Есть несколько практических различий между Globstar и
find
.find
гораздо более универсален, чем Globstar. Все, что вы можете сделать с globstar, вы можете сделать и сfind
командой. Мне нравится globstar, и иногда это удобнее, но globstar не является общей альтернативойfind
.Метод выше не ищет внутри каталогов, имена которых начинаются с
.
. Иногда вы не хотите использовать такие папки, но иногда это так.Как и в случае с обычным глобаном, оболочка создает список всех подходящих путей и передает их в качестве аргументов вашей команде (
grep
) вместо самого глобуса. Если вы называете так много файлов,file.txt
что полученная команда будет слишком длинной для выполнения системой, то приведенный выше метод завершится ошибкой. На практике вам понадобится (как минимум) тысячи таких файлов, но это может произойти.Используемые методы
find
не подпадают под это ограничение, потому что:Путь Занны строит и запускает
grep
команду с потенциально большим количеством аргументов пути. Но если найдено больше файлов, чем может быть указано в одном пути, действие+
-terminated-exec
запускает команду с некоторыми из путей, затем запускает ее снова с еще несколькими путями и так далее. В случае использованияgrep
для строки в нескольких файлах это приводит к правильному поведению.Как и описанный здесь метод globstar, он печатает все совпадающие строки с путями, добавленными к каждой.
Путь Судодуса проходит
grep
отдельно для каждогоfile.txt
найденного. Если есть много файлов, это может быть медленнее, чем некоторые другие методы, но это работает.Этот метод находит файлы и печатает их пути, после чего следуют соответствующие строки, если таковые имеются. Это формат вывода, отличный от формата, созданного моими методами, Zanna и Muru .
Получение цвета с
find
Одним из непосредственных преимуществ использования globstar является то, что по умолчанию в Ubuntu
grep
будет производить цветной вывод. Но вы можете легко получить этоfind
тоже .Учетные записи пользователей в Ubuntu создаются с псевдонимом, который
grep
действительно запускаетgrep --color=auto
(бегите,alias grep
чтобы увидеть). Это хорошая вещь , что псевдонимы в значительной степени только расширяется , когда вы выдаете их в интерактивном режиме , но это означает , что если вы хотите ,find
чтобы вызватьgrep
с--color
флагом, вы должны написать его в явном виде. Например:источник
bash
оболочку, чтобы это работало. Вы же говорите неявно в «globstar Баш варианта оболочки» , но он может быть легко пропущен людьми , читающих слишком быстро.**
глобусы с обходом каталогов, ваша основная критика верна: представление**
в этом ответе относится только к bash, где shopt - только bash, а термин «globstar» - (я думаю) bash и только tcsh. Изначально я размышлял над этим из-за этих сложностей, но вы правы, что это несколько сбивает с толку. Вместо того чтобы подробно обсуждать это в этом ответе, я привел ссылку на другой (довольно подробный) пост, который делает тяжелую работу.-e
не должно применяться к путям, но это легко исправить. Для первой команды просто опустите-e
. Для второго используйтеfind . -name file.txt -printf $'\e[32m%p:\e[0m\n' -exec grep -i "pattern" {} \;
илиfind . -name file.txt -exec printf '\e[32m%s:\e[0m\n' {} \; -exec grep -i "pattern" {} \;
. Пользователи иногда предпочитают ваш путь (с-e
фиксированным использованием) другим, которые печатают один путь на соответствующую строку ; yours печатает один путь на каждый найденный файл, за которым следуютgrep
результаты.grep
сам не буду делать то, что ты делаешь. Некоторые другие критические замечания тоже были неправильными.grep -H
бежать по-exec
желанию не раскрасить без--color
(илиGREP_COLOR
). IEEE 1003.1-2008 не гарантирует{}
расширения##### {}:
, но в Ubuntu есть GNU find, что делает . Если с вами все в порядке, я отредактирую ваше сообщение, чтобы исправить-e
ошибку (и уточнить ее вариант использования), и вы увидите, хотите ли вы отменить удаление. (У меня есть представитель для просмотра / редактирования удаленных сообщений.)Вам не нужно
find
для этого;grep
может справиться с этим совершенно нормально самостоятельно:От
man grep
:источник
find?
Метод, приведенный в ответе Муру , о беге
grep
с использованием--include
флага для указания имени файла, часто является лучшим выбором. Тем не менее, это также может быть сделано сfind
.Подход в этом ответе используется
find
для запускаgrep
отдельно для каждого найденного файла и печатает путь к каждому файлу ровно один раз. , над совпадающими строками, найденными в каждом файле. (Методы, которые печатают путь перед каждой соответствующей строкой, описаны в других ответах.)Вы можете изменить каталог на вершину дерева каталогов, где у вас есть эти файлы. Затем запустите:
При этом печатается путь (относительно текущего каталога
.
и включая само имя файла) каждого именованного файлаfile.txt
, за которым следуют все совпадающие строки в файле. Это работает, потому что{}
это заполнитель для найденного файла. Путь к каждому файлу устанавливается отдельно от его содержимого с помощью префикса#####
и печатается только один раз перед соответствующими строками из этого файла. (В вызываемых файлахfile.txt
, не содержащих совпадений, пути по-прежнему печатаются.) Вы можете обнаружить, что эти выходные данные менее загромождены, чем то, что вы получаете от методов, которые печатают путь в начале каждой совпадающей строки.Использование
find
такого почти всегда будет быстрее, чем запускgrep
на каждом файле (grep -arin "pattern" *
), потому чтоfind
ищет файлы с правильным именем и пропускает все остальные файлы.Ubuntu использует GNU find , который всегда расширяется,
{}
даже когда он появляется в большей строке , например##### {}:
. Если вам нужна ваша команда для работыfind
в системах, которые могут не поддерживать это , или вы предпочитаете использовать это-exec
действие только тогда, когда это абсолютно необходимо, вы можете использовать:Чтобы облегчить чтение выходных данных , вы можете использовать escape-последовательности ANSI для получения цветных имен файлов. Это делает заголовок пути каждого файла лучше, чем совпадающие строки, которые печатаются под ним:
Это приводит к тому, что ваша оболочка превращает управляющий код для зеленого в фактическую управляющую последовательность, которая создает зеленый цвет в терминале, и делает то же самое с управляющим кодом для обычного цвета. Эти экранированные значения передаются
find
, который использует их, когда печатает имя файла. ($'
'
цитата необходима здесь, потому чтоfind
«S-printf
действие не признает\e
для интерпретации ANSI маскирование) .Если вы предпочитаете, вы можете использовать вместо этого
-exec
с системнойprintf
командой (которая не поддерживает\e
). Итак, еще один способ сделать то же самое:источник
find abc/def/efg -name "file.txt" -type f -exec echo -e "##### {}:" \; -exec grep -i "pattern" {} \;
cd abc/def/efg
'change directory' :-)-e
опциюecho
? Это заставит его искажать любые имена файлов, которые содержат обратную косую черту. (2) Использование в{}
качестве части аргумента не гарантируется. Было бы лучше сказать-exec echo "#####" {} \;
или-exec printf "##### %s:\n" {} \;
. (3) Почему бы просто не использовать-print
или-printf
? (4) Рассмотрим такжеgrep -H
.find . -name "file.txt" -type f -exec echo -e "\0033[32m{}:\0033[0m" \; -exec grep -i "pattern" {} \;
2) Вы можете быть правы, но пока это работает для меня. 3) -print и -printf также являются альтернативами. 4) Это уже есть в основном ответе. - В любом случае, добро пожаловать с собственным ответом :-)-exec
звонка. Просто используйте,grep -H
и это напечатает имя файла (в цвете), а также соответствующий текст.Просто чтобы указать, что если условия вопроса могут быть взяты литературными, вы можете использовать прямой grep:
или
источник