Расширенная замена для UNIX/GNU «найти» (поиск файлов из командной строки)
Утилита GNU по умолчанию для поиска файлов для командной строки find
имеет массу опций, но по-прежнему не хватает некоторых, на мой взгляд, важных функций, таких как сортировка . Это также хлопотно использовать иногда.
Точно так же, как это ack
можно рассматривать как современную расширенную версию grep
с более чистым интерфейсом, может ли кто-нибудь порекомендовать аналогичную замену для find
?
(Бонусные баллы, если он также имеет функциональность, аналогичную locate
, то есть индексированный поиск.)
Воспользуйтесь оболочкой! Хотя ни одна оболочка не может сравниться find
с расширенными вариантами использования , zsh может справиться со многими, и даже bash может справиться с базовыми.
Фундаментальной особенностью find
является рекурсивный обход каталогов. Эта функция есть в современных интерактивных оболочках. В bash, ksh93, zsh и fish шаблон glob **
обозначает подкаталог на любой глубине. Таким образом, echo **/*
перечисляются те же файлы, что и find .
(за исключением того, что **/*
файлы с точками по умолчанию пропускаются), echo **/*.txt
перечисляются те же файлы find -name '*.txt
, что и и т. д. Все оболочки сортируют совпадения с подстановочными знаками. Чтобы запустить команду, вы просто передаете ей аргументы, как обычно: вместо find -name '*.txt' -exec ls -ld {} +
, вы можете просто запустить ls -ld **/*.txt
.
В bash **
шаблон должен быть включен с помощью shopt -s globstar
. Поместите эту строку в свой ~/.bashrc
. В ksh93 **
шаблон должен быть включен с помощью set -o globstar
. В fish и zsh всегда **
включен.
Синтаксис оболочки более прост, когда вы хотите выполнить команду над файлами. Если вы хотите перечислить имена файлов, echo
разделите их пробелами. Если вам нужен один файл в строке, вы можете использовать
printf '%s\n' **/*
Это немного громоздко вводить, но вы можете определить псевдоним, например
alias p='printf %s\\n'
Ksh определяет дополнительный синтаксис шаблона подстановочных знаков, который дает совпадению с подстановочными знаками ту же выразительную силу, что и регулярное выражение, а также ярлык для отрицания. В bash эти шаблоны можно включить с помощью shopt -s extglob
; в зш, с setopt ksh_glob
. В Zsh есть дополнительные операторы с такой же выразительной силой, которые необходимо активировать с помощью setopt extended_glob
(вставьте setopt extended_glob
в свой ~/.zshrc
). Вот несколько примеров дополнительных шаблонов zsh (многие из них требуют setopt extended_glob
):
*-<8-15>.txt
— сопоставлять файлы с числовым индексом от 8 до 15, за которым следует .txt
. Например, это включает foo-8.txt
и, foo-08.txt
но не foo-100.txt
или foo-a.txt
. Ничего подобного в find
.*.txt~foo.txt
— соответствует всем .txt
файлам, кроме foo.txt
. Как find -name '*.txt' ! -name foo.txt
.*.(cc|cxx)
— матч .cc
и .cxx
файлы. Как find \( -name '*.cc' -o -name '*.cxx' \)
.Имейте в виду, что в bash ≤4.2 и fish **/
проходят символические ссылки на каталоги (например find -L
, ). Ksh93, bash ≥4.3 и zsh безопасны: **/
только обход каталогов. В zsh ***/
также проходит символические ссылки на каталоги.
В большинстве оболочек сопоставление подстановочных знаков основано только на имени файла. Единственный способ сопоставить файлы по типу — добавить /
в конце шаблона, что ограничивает совпадения каталогами (включая символические ссылки). Zsh уникален тем, что предлагает способ сопоставления файлов по типу и другим метаданным: квалификаторы glob . Вы даже можете встроить произвольный код в квалификаторы glob, чтобы запустить код для фильтрации и перезаписи совпадений, хотя, когда все становится настолько сложным, синтаксис, возможно, хуже, чем find
. Вот несколько примеров простого использования квалификаторов glob. Напомним, что это функция только для zsh.
**/*(.)
— все обычные файлы, напримерfind -type f
*(.)
— все обычные файлы в текущем каталоге, напримерfind -maxdepth 1 -type f
**/*(.*)
— исполняемые обычные файлы, напримерfind -type f -executable
**/*(U)
— файлы, принадлежащие вызывающему пользователю, напримерfind -user $USER
**/*(.m-2)
— обычные файлы, измененные менее 2 дней назад, напримерfind -type f -mtime -2
*(/^F)
— пустые подкаталоги текущего каталога, напримерfind -mindepth 1 -maxdepth 1 -type d -empty
Вы также можете управлять сортировкой совпадений с помощью квалификатора glob. Например, echo *(.m-2om)
печатает имена файлов, которые были изменены за последние 2 дня, от самых младших до самых старых.
Оболочки пропускают точечные файлы из совпадений с подстановочными знаками, если .
в шаблоне нет начального. Это можно контролировать с помощью FIGNORE
переменной в ksh, GLOBIGNORE
переменной в bash или glob_dots
опции в zsh. В zsh вы можете использовать D
квалификатор glob для включения точечных файлов. В отличие от других оболочек, zsh никогда не будет включать совпадения .
с ..
подстановочными знаками.
Если вы хотите исключить некоторые каталоги из обхода, в zsh вы можете поставить косую черту под звездой Клини . Например, find -name .svn -prune -o -type f
становится(^.svn/)#*(.)
Команда find
остается приоритетной, если совпадений очень много , поскольку она распечатывает файлы, как только они будут найдены, и выполняет команды по отдельности ( -exec … \;
) или в пакетах ( -exec … +
). Поскольку оболочка сортирует совпадения, она не может начать их отображение, пока не соберет их все, что может занять много времени. В последних версиях zsh есть Y
квалификатор glob для остановки после определенного количества совпадений, например, **/*.txt(Y1)
поиск .txt
файла в любом месте текущего каталога и остановка после его нахождения.
Если вы хотите выполнить команду над файлами, а оболочка жалуется, что командная строка слишком длинная, вы все равно можете использовать xargs
. Zsh предлагает зарги в качестве альтернативы.
find -exec mycommand {} +
find -print0 | xargs -0 mycommand
printf %s\\0 **/* | xargs -0 mycommand
zargs -- **/* -- mycommand
Я не знаю ничего, что интегрируется find
с locate
.
Тотти
смхайдрих
sed
, интеграция в скрипт и т. д.). Но спасибо за ваше предложение, я тоже посмотрю на это.Xen2050
| sort
, он может даже обрабатывать имена с завершением NUL с помощью--files0-from=-