Гаразд, застосуємо філософію 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'\')
застосовує CODE до кожного матчу PATTERN. Ім'я відповідного файлу в $REPLY
. Встановлення reply
масиву перетворює збіг у список імен.
$REPLY\#
- це перегляд каталогу архіву.
$REPLY\#/**/*.pm
відповідає .pm
файлам в архіві.
N
Глоб класифікатор робить шаблон розширення порожній список , якщо збігу немає.