Jak rekurencyjnie przeglądać skompresowane archiwa?

16

Próbuję dowiedzieć się, jakie moduły use Test::Versionw cpan. Więc zwykłem minicpanto odzwierciedlać. Mój problem polega na tym, że muszę iterować pobrane archiwa i przeglądać pliki znajdujące się w archiwach. Czy ktoś może mi powiedzieć, jak to zrobić? najlepiej w sposób, który mówi mi, który plik w archiwum i jaką linię ma.

(uwaga: nie wszystkie są archiwami, niektóre są plikami zip)

ksenoterracid
źródło

Odpowiedzi:

18

Ok, zastosujmy filozofię unix. Jakie są składniki tego zadania?

  • Wyszukiwanie tekstu: potrzebujesz narzędzia do wyszukiwania tekstu w pliku, takiego jak grep.
  • Rekurencyjne: potrzebujesz narzędzia do szukania plików w drzewie katalogów, takich jak find.
  • Archiwa: potrzebujesz narzędzia do ich odczytu.

Większość programów uniksowych działa na plikach. Aby więc łatwo obsługiwać komponenty archiwalne, musisz uzyskać do nich dostęp jako pliki, innymi słowy, musisz uzyskać do nich dostęp jako katalogi.

System plików AVFS przedstawia widok systemu plików, w którym każdy plik archiwum /path/to/foo.zipjest dostępny jako katalog ~/.avfs/path/to/foo/zip#. System AVFS zapewnia dostęp tylko do odczytu do najpopularniejszych formatów plików archiwów.

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

Objaśnienia:

  • Zamontuj system plików AVFS.
  • Poszukaj plików archiwum w ~/.avfs$PWD, który jest widokiem AVFS bieżącego katalogu.
  • Dla każdego archiwum wykonaj określony fragment powłoki (z $0= nazwa archiwum i $1= wzorzec do przeszukania).
  • $0#to widok katalogu archiwum $0.
  • {\}raczej niż {}jest potrzebna w przypadku zewnętrzne findsubstytuty {}wewnątrz -exec ;argumentów (niektóre to zrobić, niektóre nie).
  • Opcjonalnie: w końcu odmontuj system plików AVFS.

Lub w zsh ≥4,3:

mountavfs
grep 'Test::Version' ~/.avfs$PWD/**/*.(tgz|tar.gz|zip)(e\''
     reply=($REPLY\#/**/*.pm(.N))
'\')

Objaśnienia:

  • ~/.avfs$PWD/**/*.(tgz|tar.gz|zip) dopasowuje archiwa w widoku AVFS bieżącego katalogu i jego podkatalogów.
  • PATTERN(e\''CODE'\')stosuje KOD do każdego dopasowania WZORU. Nazwa dopasowanego pliku znajduje się w $REPLY. Ustawienie replytablicy zmienia dopasowanie w listę nazw.
  • $REPLY\# to widok katalogu archiwum.
  • $REPLY\#/**/*.pmdopasowuje .pmpliki w archiwum.
  • NGlob kwalifikator sprawia, że wzór poszerzyć do pustej listy, jeśli nie ma odpowiednika.
Gilles „SO- przestań być zły”
źródło
stwarza to kolejny interesujący problem z koniecznością montowania, a następnie odmontowywania wszystkich archiwów, ponieważ część problemu polega na tym, że istnieją 22 tys. archiwów, które należy przeszukać
xenoterracide
@xenoterracide: Jak to problem? W systemie AVFS masz jeden punkt podłączenia ( ~/.avfs), a dostęp do każdego archiwum jest automatyczny ( ~/.avfs/path/to/archive.zip\#jest to zwykły katalog w systemie plików AVFS, a nie punkt podłączenia). Oczywiście każde archiwum, do którego uzyskujesz dostęp, oznacza niewielki spadek wydajności, ale jest to nieodłączny problem.
Gilles „SO - przestań być zły”
@Gilles tylko fakt, że teraz muszę przejść i wymyślić, jak je najpierw zamontować, co wydaje się trochę złym pomysłem, lepiej montować je podczas i odmontowywać po przeszukaniu.
ksenoterrakid
@xenoterracide: Ponownie: nie, nie trzeba montować ich indywidualnie. Pełny przepływ pracy (oprócz instalowania systemu AVFS w razie potrzeby) znajduje się w moich fragmentach kodu.
Gilles „SO- przestań być zły”
@Gilles no cóż, będę musiał się trochę w to find: missing argument to zagłębić ... bo dostaję -exec''i dużo tego od zshzsh: Input/output error: Data-Maker-0.27
ksenoterracyd
0

Wygląda na to, że mogę to zrobić w ten sposób

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

Daje to jednak wyniki takie jak:

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

co nie jest bardzo specyficzne dla tego, gdzie w tarball. Mam nadzieję, że ktoś wymyśli lepszą odpowiedź.

ksenoterracid
źródło
0

Dzięki za wyzwanie wymyśliłem:

#!/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
Kyle Smith
źródło
Właśnie zobaczyłem wymagania dotyczące numeru linii. To prawdopodobnie działa z pewną kombinacją grep -n i awk, aby przechwycić numer linii. Nie może być tak proste jak grep -H, aby wyświetlić nazwę pliku, ponieważ zawsze jest to standardowe, więc może wymagać więcej linii.
Kyle Smith
błędy 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
wystąpiły
również nie zdawałem sobie sprawy, kiedy po raz pierwszy to opublikowałem, że niektóre archiwa na cpan to pliki zip.
ksenoterrakid
Hm, testowałem ze strukturą tylko plików .tar.gz - może być bardziej niezawodny, aby podejmować odpowiednie działania w zależności od typu pliku, ale to powinno dać dobry punkt wyjścia.
Kyle Smith
0

Może moja odpowiedź będzie dla kogoś pomocna:

#!/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
Serge Roussak
źródło
0

Po instalacji p7zip-*możesz to zrobić:

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

Nie musisz używać lsprzed pierwszym potokiem, bez względu na listę skompresowane pliki będą działać. Ostateczne lesstylko pokaże ŚCIEŻKĘ życia listet w skompresowanym archiwum, ale nie nazwa tego.

Roberto Robert
źródło
0

Użyj find, aby zlokalizować wszystkie niezbędne pliki, a następnie zgrep, aby przejrzeć skompresowane pliki:

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

Nie przetestowałem tego jednak na tarballach

Iggy Pop
źródło