Как я могу рекурсивно grep через сжатые архивы?


16

Я пытаюсь выяснить, какие модули use Test::Versionв cpan. Так что я привык minicpanотражать это. Моя проблема заключается в том, что мне нужно перебирать загружаемые архивы и выполнять поиск файлов в архивах. Может кто-нибудь сказать мне, как я мог бы сделать это? желательно таким образом, чтобы указать, какой файл в архиве и в какой строке он находится.

(примечание: они не все tarballs, некоторые из них являются zip-файлами)

Ответы:


18

Хорошо, давайте применим философию 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 файлы в архиве.
  • Спецификатор Nglob расширяет шаблон до пустого списка, если совпадений нет.

это создает другую интересную проблему необходимости монтировать и затем размонтировать все архивы, так как часть проблемы заключается в том, что есть 22k архивов, которые необходимо найти
xenoterracide

@xenoterracide: Как это проблема? С AVFS у вас есть одна точка монтирования ( ~/.avfs), и доступ к каждому архиву происходит автоматически ( ~/.avfs/path/to/archive.zip\#это обычный каталог в файловой системе AVFS, а не точка монтирования). Конечно, каждый доступ к архиву означает небольшое снижение производительности, но это неотъемлемая часть проблемы.
Жиль "ТАК - перестань быть злым"

@Gilles только тот факт, что теперь мне нужно пройти и выяснить, как их сначала монтировать, что кажется немного плохой идеей, лучше монтировать их, когда я иду, и демонтировать после поиска.
ксенотеррацид

@xenoterracide: Опять же: нет, вам не нужно монтировать их отдельно. Полный рабочий процесс (кроме установки AVFS, если необходимо) приведен в моих фрагментах кода.
Жиль "ТАК - перестань быть злым"

@ gilles ну, мне придется немного покопаться в этом ... потому что я получаю find: missing argument to -exec'` и многое из этого из zshzsh: Input/output error: Data-Maker-0.27
xenoterracide

0

Похоже, я могу сделать это так

find authors/ -type f -exec zgrep "Test::Version" '{}' +  

Тем не менее, это дает такие результаты:

authors/id/J/JO/JONASBN/Module-Info-File-0.11.tar.gz:Binary file (standard input) matches

что не очень конкретно, где в tarball. Надеюсь, кто-то может придумать лучший ответ.


0

Спасибо за вызов, я придумал:

#!/bin/bash
#

# tarballs to check in
find authors/ -type f | while read tarball; do

    # get list of files in tarball (not dirs ending in /):
    tar tzf $tarball | grep -v '/$' | while read file; do       

        # get contents of file and look for string
        tar -Ozxf conform.tar.gz $file | grep -q 'Text::Version' && echo "Tar ($tarball) has matching File ($file)"

    done

done

Только что увидел ваше требование номера строки. Это, вероятно, может работать с некоторой комбинацией grep -n и awk для захвата номера строки. Не может быть так просто, как grep -H, чтобы вывести список имен файлов, так как это всегда stdin, поэтому может потребоваться больше строк.
Кайл Смит

ошибки при запуске в моей системе бесконечно повторяются:tar (child): conform.tar.gz: Cannot open: No such file or directory tar (child): Error is not recoverable: exiting now tar: Child returned status 2 tar: Error is not recoverable: exiting now
xenoterracide

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

Хм, я протестировал со структурой только файлов .tar.gz - можно было бы сделать более надежным выполнение соответствующих действий в зависимости от типа файла, но это должно дать достойную отправную точку.
Кайл Смит

0

Может быть, мой ответ будет полезным для кого-то:

#!/bin/bash

findpath=$(echo $1 | sed -r 's|(.*[^/]$)|\1/|')

# tarballs to check in
find $findpath -type f | while read tarball; do

    # get list of files in tarball (not dirs ending in /):
    if [ -n "$(file --mime-type $tarball | grep -e "application/jar")" ]; then

        jar tf $tarball | grep -v '/$' | while read file; do
            # get contents of file and look for string
            grepout=$(unzip -q -c $tarball $file | grep $3 -e "$2")

            if [ -n "$grepout" ]; then
                echo "*** $tarball has matching file ($file):"
                echo $grepout
            fi

        done

    elif tar -tf $tarball 2>/dev/null; then

        tar -tf $tarball | grep -v '/$' | while read file; do
            # get contents of file and look for string
            grepout=$(unzip -q -c $tarball $file | grep $3 -e "$2")

            if [ -n "$grepout" ]; then
                echo "*** $tarball has matching file ($file):"
                echo $grepout
            fi

        done

    else
        file=""
        grepout=$(grep $3 -e "$2" $tarball)

        if [ -n "$grepout" ]; then
            echo "*** $tarball has matching:"
            echo $grepout
        fi

    fi

done

0

После установки p7zip-*вы можете сделать это:

ls | xargs -I {} 7z l {} | grep whatever | less

Вам не нужно использовать lsперед первым каналом, какой бы список сжатые файлы не работали. Только финал lessпокажет ПУТЬ жизни листета внутри сжатого архива, но не имя этого.


0

Используйте find для поиска всех необходимых файлов, а zgrep - для просмотра сжатых файлов:

find <folder> -type f -name "<search criteria[*gz,*bz...]>" -execdir zgrep -in "<grep expression>" '{}' ';'

Не проверял это на tarballs, хотя

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