Поиск рекурсивно всех архивных файлов различных форматов и поиск их по шаблонам имен файлов


11

В лучшем случае я хотел бы иметь такой звонок:

$searchtool /path/to/search/ -contained-file-name "*vacation*jpg"

... так что этот инструмент

  • выполняет рекурсивное сканирование заданного пути
  • принимает все файлы с поддерживаемыми форматами архивов, которые должны быть как минимум «наиболее распространенными», такими как zip, rar, 7z, tar.bz, tar.gz ...
  • и отсканируйте список файлов архива для рассматриваемого шаблона имени (здесь *vacation*jpg)

Я знаю, как использовать инструмент поиска, tar, unzip и тому подобное. Я мог бы объединить их с помощью сценария оболочки, но я ищу простое решение, которое может быть однострочным или специализированным инструментом (подсказки для инструментов с графическим интерфейсом приветствуются, но мое решение должно основываться на командной строке).

Ответы:


9

(Адаптировано из Как рекурсивно grep через сжатые архивы? )

Установите AVFS , файловую систему, которая обеспечивает прозрачный доступ внутри архивов. Сначала выполните эту команду один раз, чтобы настроить вид файловой системы вашего компьютера, в которой вы можете получить доступ к архивам, как если бы они были каталогами:

mountavfs

После этого, если /path/to/archive.zipэто распознанный архив, то ~/.avfs/path/to/archive.zip#это каталог, в котором содержится содержимое архива.

find ~/.avfs"$PWD" \( -name '*.7z' -o -name '*.zip' -o -name '*.tar.gz' -o -name '*.tgz' \) \
     -exec sh -c '
                  find "$0#" -name "*vacation*.jpg"
                 ' {} 'Test::Version' \;

Пояснения:

  • Смонтируйте файловую систему AVFS.
  • Ищите архивные файлы в ~/.avfs$PWD, который является представлением AVFS текущего каталога.
  • Для каждого архива выполните указанный фрагмент оболочки (с $0= имя архива и $1= шаблон для поиска).
  • $0#это каталог просмотра архива $0.
  • {\}а не {}требуется в случае , если внешние findЗаменители {}внутри -exec ;аргументов (некоторые делают это, некоторые нет).

Или в zsh ≥4.3:

mountavfs
ls -l ~/.avfs$PWD/**/*.(7z|tgz|tar.gz|zip)(e\''
     reply=($REPLY\#/**/*vacation*.jpg(.N))
'\')

Пояснения:

  • ~/.avfs$PWD/**/*.(7z|tgz|tar.gz|zip) соответствует архивам в представлении AVFS текущего каталога и его подкаталогов.
  • PATTERN(e\''CODE'\')применяет код для каждого совпадения PATTERN. Имя соответствующего файла находится в $REPLY. Установка replyмассива превращает совпадение в список имен.
  • $REPLY\# это каталог просмотра архива.
  • $REPLY\#/**/*vacation*.jpgсопоставляет *vacation*.jpgфайлы в архиве.
  • Спецификатор Nglob расширяет шаблон до пустого списка, если совпадений нет.

9

Если вы хотите что-то более простое, чем решение AVFS, я написал скрипт Python для этого под названием arkfind . Вы можете просто сделать

$ arkfind /path/to/search/ -g "*vacation*jpg"

Это будет делать это рекурсивно, поэтому вы можете просматривать архивы внутри архивов с произвольной глубиной.


Спасибо, хороший вклад! Особенно, если AVFS не вариант.
mdo

Было бы здорово, если он поддерживает файлы JAR.
Chemik

@ Chemik - отметил ! Я сделаю немного больше над этим в эти выходные :) JAR не должен быть слишком сложным, я считаю, что это действительно просто почтовый файл для внешнего мира.
13

@ Chemik - я только что попробовал, и он должен поддерживать файлы JAR в его текущей форме в любом случае. Можете ли вы проверить это, и если это не сработает, как вы ожидаете, сообщите об ошибке на странице Github? (Я только что
исправил

1
Да, теперь я вижу, это работает. Вы можете добавить «JAR-файлы» в README :)
Chemik

2

Мое обычное решение:

find -iname '*.zip' -exec unzip -l {} \; 2>/dev/null | grep '\.zip\|DESIRED_FILE_TO_SEARCH'

Пример:

find -iname '*.zip' -exec unzip -l {} \; 2>/dev/null | grep '\.zip\|characterize.txt'

Resuls похожи на:

foozip1.zip:
foozip2.zip:
foozip3.zip:
    DESIRED_FILE_TO_SEARCH
foozip4.zip:
...

Если вы хотите только zip-файл с попаданиями :

find -iname '*.zip' -exec unzip -l {} \; 2>/dev/null | grep '\.zip\|FILENAME' | grep -B1 'FILENAME'

FILENAME здесь используется дважды, поэтому вы можете использовать переменную.

С помощью find вы можете использовать PATH / TO / SEARCH


2

Другое решение, которое работает zgrep

zgrep -r filename *.zip

1
Что это за реализация zgrep? Это не работает с тем, что поставляется с GNU gzip( /bin/zgrep: -r: option not supported, zgrep (gzip) 1.6)
Стефан Шазелас

2

ИМХО удобство работы также должно быть в bash:

 while read -r zip_file ; do echo "$zip_file" ; unzip -l "$zip_file" | \
 grep -i --color=always -R "$to_srch"; \
 done < <(find . \( -name '*.7z' -o -name '*.zip' \)) | \
 less -R

и для гудрона (этот не проверен ...)

 while read -r tar_file ; do echo "$tar_file" ; tar -tf  "$tar_file" | \
 grep -i --color=always -R "$to_srch"; \
 done < <(find . \( -name '*.tar.gz' -o -name '*.tar' \)) | \
 less -R

Какая unzipреализация может работать с файлами 7z или tar.gz?
Стефан Шазелас

да, это ошибка ... исправлена ​​... нужно определенно использовать правильные двоичные файлы для правильных типов файлов ... я просто стремился продемонстрировать однострочную оболочку .. дже этот почти достигнет состояния готовности как-как квитанция ...
Йордан Георгиев

0

libarchive«s bsdtarможет обрабатывать большинство из этих форматов файлов, так что вы можете сделать:

find . \( -name '*.zip' -o     \
          -name '*.tar' -o     \
          -name '*.tar.gz' -o  \
          -name '*.tar.bz2' -o \
          -name '*.tar.xz' -o  \
          -name '*.tgz' -o     \
          -name '*.tbz2' -o    \
          -name '*.7z' -o      \
          -name '*.iso' -o     \
          -name '*.cpio' -o    \
          -name '*.a' -o       \
          -name '*.ar' \)      \
       -type f                 \
       -exec bsdtar tf {} '*vacation*jpg' \; 2> /dev/null

Что вы можете упростить (и улучшить, чтобы сопоставить без учета регистра) с GNU find:

find . -regextype egrep \
       -iregex '.*\.(zip|7z|iso|cpio|ar?|tar(|\.[gx]z|\.bz2)|tgz|tbz2)' \
       -type f \
       -exec bsdtar tf {} '*vacation*jpg' \; 2> /dev/null

Это не печатает путь архива, где эти *vacation*jpgфайлы найдены все же. Чтобы напечатать это имя, вы можете заменить последнюю строку на:

-exec sh -ac '
   for ARCHIVE do
     bsdtar tf "$ARCHIVE" "*vacation*jpg" |
       awk '\''{print ENVIRON["ARCHIVE"] ": " $0}'\''
   done' sh {} + 2> /dev/null

который дает вывод как:

./a.zip: foo/blah_vacation.jpg
./a.zip: bar/blih_vacation.jpg
./a.tar.gz: foo/blah_vacation.jpg
./a.tar.gz: bar/blih_vacation.jpg

Или с zsh:

setopt extendedglob # best in ~/.zshrc
for archive (**/*.(#i)(zip|7z|iso|cpio|a|ar|tar(|.gz|.xz|.bz2)|tgz|tbz2)(.ND)) {
  matches=("${(f@)$(bsdtar tf $archive '*vacation*jpg' 2> /dev/null)"})
  (($#matches)) && printf '%s\n' "$archive: "$^matches
}

Обратите внимание, что есть ряд других форматов файлов, которые являются просто zipили tgzзамаскированными файлами типа .jarили .docxфайлами. Вы можете добавить их в свой шаблон find/ zshsearch, bsdtarне заботясь о расширении (например, он не использует расширение для определения типа файла).

Обратите внимание, что *vacation*.jpgприведенное выше сопоставляется с полным путем к элементу архива, а не только с именем файла, поэтому он будет совпадать с включенным, vacation.jpgно также и с включенным vacation/2014/file.jpg.

Чтобы сопоставить только имя файла, можно использовать режим извлечения , использовать -s(замену), который использует регулярные выражения с pфлагом для печати имен соответствующих файлов, а затем убедиться, что ни один файл не извлечен, например:

bsdtar -'s|.*vacation[^/]*$||' -'s|.*||' -xf "$archive"

Обратите внимание, что он выведет список на stderr и добавит >>к каждой строке. В любом случае, bsdtarкак и в большинстве tarреализаций, могут отображаться имена файлов на дисплее, если они содержат некоторые символы, такие как символ новой строки или обратный слеш (отображается как \nили \\).

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.