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

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"

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

detly
источник
Спасибо, хороший вклад! Особенно, если 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или \\).

Стефан Шазелас
источник