Znajdź rekurencyjnie wszystkie pliki archiwów o różnych formatach archiwów i wyszukaj wzorce nazw plików

11

W najlepszym razie chciałbym mieć taki telefon:

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

... aby to narzędzie

  • wykonuje rekursywny skan danej ścieżki
  • pobiera wszystkie pliki z obsługiwanymi formatami archiwów, które powinny być co najmniej „najbardziej powszechne”, takie jak zip, rar, 7z, tar.bz, tar.gz ...
  • i zeskanuj listę plików archiwum w poszukiwaniu wzorca nazwy (tutaj *vacation*jpg)

Wiem, jak korzystać z narzędzia wyszukiwania, tarowania, rozpakowywania i tym podobnych. Mógłbym połączyć je ze skryptem powłoki, ale szukam prostego rozwiązania, które mogłoby być powłoką jednowierszową lub dedykowanym narzędziem (podpowiedzi do narzędzi GUI są mile widziane, ale moje rozwiązanie musi być oparte na linii poleceń).

mdo
źródło

Odpowiedzi:

9

(Na podstawie Jak rekurencyjnie przeglądać skompresowane archiwa? )

Zainstaluj system plików AVFS , który zapewnia przejrzysty dostęp do archiwów. Najpierw uruchom to polecenie, aby skonfigurować widok systemu plików komputera, w którym możesz uzyskać dostęp do archiwów tak, jakby były katalogami:

mountavfs

Po tym, jeśli /path/to/archive.zipjest rozpoznanym archiwum, to ~/.avfs/path/to/archive.zip#jest katalog, który wydaje się zawierać zawartość archiwum.

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' \;

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).

Lub w zsh ≥4,3:

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

Objaśnienia:

  • ~/.avfs$PWD/**/*.(7z|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\#/**/*vacation*.jpgdopasowuje *vacation*.jpgpliki 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
9

Jeśli chcesz czegoś prostszego niż rozwiązanie AVFS, napisałem w tym celu skrypt o nazwie arkfind . Możesz po prostu zrobić

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

Zrobi to rekurencyjnie, abyś mógł przeglądać archiwa wewnątrz archiwów na dowolnej głębokości.

obrzydliwie
źródło
Dzięki, niezły wkład! Zwłaszcza jeśli AVFS nie jest opcją.
mdo
Byłoby wspaniale, gdyby wspierał pliki jar.
Chemik,
@Chemik - odnotowano ! W ten weekend wykonam trochę więcej pracy :) JAR nie powinien być zbyt trudny, uważam, że to naprawdę plik zip do świata zewnętrznego.
detly
@Chemik - właśnie go wypróbowałem i tak powinien obsługiwać pliki JAR w obecnej formie. Czy możesz to przetestować, a jeśli to nie działa zgodnie z oczekiwaniami, zgłoś błąd na stronie Github? (Właśnie naprawiłem błąd, więc zaktualizuj swoją kopię.)
detly
1
Tak, widzę teraz, to działa. Możesz dodać „pliki JAR” do README :)
Chemik
2

Moje zwykłe rozwiązanie:

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

Przykład:

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

Rezultaty są jak:

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

Jeśli chcesz tylko plik zip z trafieniami :

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

FILENAME tutaj jest używany dwukrotnie, więc możesz użyć zmiennej.

Z Find możesz użyć PATH / TO / SEARCH

Rodrigo Gurgel
źródło
2

Innym rozwiązaniem, które działa zgrep

zgrep -r filename *.zip
John Oxley
źródło
1
Jakie to wdrożenie zgrep? To nie działa z tym dostarczonym z GNU gzip( /bin/zgrep: -r: option not supported, zgrep (gzip) 1.6)
Stéphane Chazelas,
2

Przyjazność dla użytkowników IMHO powinna być również czymś wyjątkowym:

 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

i dla smoły (ten jest niesprawdzony ...)

 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
Yordan Georgiev
źródło
Jaka unzipimplementacja może poradzić sobie z plikami 7z lub tar.gz?
Stéphane Chazelas,
tak, to jest błąd ... poprawiony ... zdecydowanie powinieneś użyć poprawnych plików binarnych dla poprawnych typów plików ... Chciałem tylko zademonstrować jedną linię. Jezu, ten prawie osiągnie stan gotowości jako dowód odbioru ...
Yordan Georgiev
0

libarchive„s bsdtarmoże obsługiwać większość z tych formatów, więc można zrobić:

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

Które możesz uprościć (i ulepszyć, aby dopasować bez rozróżniania wielkości liter) z GNU za findpomocą:

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

Nie powoduje to jednak wydrukowania ścieżki do archiwum, w którym znajdują się te *vacation*jpgpliki. Aby wydrukować tę nazwę, możesz zastąpić ostatni wiersz:

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

co daje wynik taki jak:

./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

Lub z 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
}

Należy pamiętać, że istnieje wiele innych formatów plików, które są po prostu ziplub tgzpliki w przebraniu, takie jak .jarlub .docxpliki. Możesz dodać je do wzorca find/ zshwyszukiwania, bsdtarnie przejmuje się rozszerzeniem (jak w, to nie zależy od rozszerzenia, aby określić typ pliku).

Zauważ, że *vacation*.jpgpowyższe jest dopasowane do pełnej ścieżki członka archiwum, nie tylko do nazwy pliku, więc pasowałoby do, vacation.jpgale także do vacation/2014/file.jpg.

Aby dopasować tylko nazwę pliku, jedną sztuczką byłoby użycie trybu wypakowania , użycie -s(podstawienie), które używa wyrażeń regularnych z pflagą do wydrukowania nazw pasujących plików, a następnie upewnienie się, że żaden plik nie jest wyodrębniony, na przykład:

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

Zauważ, że wypisuje listę na stderr i dołącza >>do każdej linii. W każdym razie, bsdtarpodobnie jak większość tarimplementacji, może zmieniać nazwy wyświetlanych plików, jeśli zawierają one znaki takie jak znak nowej linii lub ukośnik odwrotny (renderowane jako \nlub \\).

Stéphane Chazelas
źródło