Хорошо, давайте применим философию Unix. Каковы компоненты этой задачи?
- Поиск текста: вам нужен инструмент для поиска текста в файле, например
grep
.
- Рекурсивно: вам нужен инструмент для поиска файлов в дереве каталогов, например
find
.
- Архивы: вам нужен инструмент для их чтения.
Большинство unix-программ работают с файлами. Таким образом, чтобы легко работать с компонентами архива, вам нужно обращаться к ним как к файлам, другими словами, вам нужно обращаться к ним как к каталогам.
В АВФ файловая система представляет собой вид файловой системы , где каждый архивный файл /path/to/foo.zip
доступен как каталог ~/.avfs/path/to/foo/zip#
. AVFS обеспечивает доступ только для чтения к большинству распространенных форматов архивных файлов.
mountavfs
find ~/.avfs"$PWD" \( -name '*.zip' -o -name '*.tar.gz' -o -name '*.tgz' \) \
-exec sh -c '
find "$0#" -name "*.pm" -exec grep "$1" {\} +
' {} 'Test::Version' \;
fusermount -u ~/.avfs # optional
Пояснения:
- Смонтируйте файловую систему AVFS.
- Ищите архивные файлы в
~/.avfs$PWD
, который является представлением AVFS текущего каталога.
- Для каждого архива выполните указанный фрагмент оболочки (с
$0
= имя архива и $1
= шаблон для поиска).
$0#
это каталог просмотра архива $0
.
{\}
скорее, чем {}
требуется в случае , если внешние find
Заменители {}
внутри -exec ;
аргументов (некоторые делают это, некоторые нет).
- Необязательно: наконец размонтируйте файловую систему AVFS.
Или в zsh ≥4.3:
mountavfs
grep 'Test::Version' ~/.avfs$PWD/**/*.(tgz|tar.gz|zip)(e\''
reply=($REPLY\#/**/*.pm(.N))
'\')
Пояснения:
~/.avfs$PWD/**/*.(tgz|tar.gz|zip)
соответствует архивам в представлении AVFS текущего каталога и его подкаталогов.
PATTERN(e\''CODE'\')
применяет код для каждого совпадения PATTERN. Имя соответствующего файла находится в $REPLY
. Установка reply
массива превращает совпадение в список имен.
$REPLY\#
это каталог просмотра архива.
$REPLY\#/**/*.pm
Спички .pm
файлы в архиве.
- Спецификатор
N
glob расширяет шаблон до пустого списка, если совпадений нет.