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

16

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

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

xenoterracide
источник

Ответы:

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. Надеюсь, кто-то может придумать лучший ответ.

xenoterracide
источник
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, хотя

Игги Поп
источник