Rozpakowywanie plików przelatujących przez potok

39

Czy mogę sprawić, aby rozpakowanie lub podobne programy działały na standardowym wyjściu? Sytuacja polega na tym, że pobieram plik zip, który powinien być rozpakowany w locie.

Powiązany problem: Jak potokować pobrany plik do standardowego wyjścia w bash?

Alex
źródło
Wydawało się, że powinno to być wykonalne, ale wygląda na to, że można rozpakować zip i przesłać plik do innego polecenia, jeśli zip zawiera tylko jeden plik. Chciałem wyodrębnić określony plik z pliku zip zawierającego wiele plików. Zamiast pipowania przełączyłem się na tworzenie łańcuchów wielu poleceń „rozpakuj plik.zip / ścieżka / plik i& dostuff / ścieżka / plik i&rm -rf / ścieżka” Nie odpowiadając na oryginalne pytanie i powodując utworzenie plików tymczasowych, spełniło moje potrzeba.
Stan Kurdziel
Sprawdź Pigz. Używamy go w rurze. andrew.tumblr.com/post/2316602611
dmourati

Odpowiedzi:

22

Chociaż plik zip jest w rzeczywistości formatem kontenera, nie ma powodu, dla którego nie można go odczytać z potoku (standardowego wejścia), jeśli plik można łatwo dopasować do pamięci. Oto skrypt w języku Python, który pobiera plik zip jako standardowe wejście i wypakowuje zawartość do bieżącego katalogu lub do określonego katalogu, jeśli jest określony.

import zipfile
import sys
import StringIO
data = StringIO.StringIO(sys.stdin.read())
z = zipfile.ZipFile(data)
dest = sys.argv[1] if len(sys.argv) == 2 else '.'
z.extractall(dest)

Skrypt ten można zminimalizować do jednej linii i utworzyć jako alias.

alias unzip-stdin="python -c \"import zipfile,sys,StringIO;zipfile.ZipFile(StringIO.StringIO(sys.stdin.read())).extractall(sys.argv[1] if len(sys.argv) == 2 else '.')\""

Teraz łatwo rozpakuj wyjście wget.

wget http://your.domain.com/your/file.zip -O - | unzip-stdin target_dir
Jason R. Coombs
źródło
1
Ty i python rock !!!
Farid Nouri Neshat
3
Przyjemny jednowarstwowy i +1 za wzmiankę, że plik musi pasować do pamięci. (Niestety nie ma sposobu na rozpakowanie pliku pkzip ze względu na strukturę formatu pliku).
lxgr
2
pamiętaj, że buforuje wszystko w pamięci przed wypakowaniem
William Casarin
1
nie ma powodu, dla którego nie można go odczytać jako strumienia, jeśli plik łatwo mieści się w pamięci, nie jest naprawdę dokładny. Powodem, dla którego musisz buforować całe archiwum zip w pamięci przed wypakowaniem zawartości, jest to, że nie można jej odczytać jako strumienia. Oczywiście nadal przydatne może być uniknięcie zapisywania archiwum zip do pliku.
Håkan Lindqvist
To nie jest strumień, czytasz cały plik w pamięci za pomocą .read()metody
Romuald Brunet
17

Jest mało prawdopodobne, aby działało zgodnie z oczekiwaniami. Zip to nie tylko format kompresji, ale także format kontenera. Łączy zadania zarówno tar, jak i gzip.bzip2 w jednym. Powiedziawszy to, jeśli twój zip ma jeden plik, możesz użyć rozpakowania -p, aby wyodrębnić pliki na standardowe wyjście. Jeśli masz więcej niż jeden plik, nie ma sposobu, aby powiedzieć, gdzie zaczynają się i kończą.

Jeśli chodzi o czytanie ze stdin, strona podręcznika rozpakowania ma następujące zdanie:

Archiwa odczytane ze standardowego wejścia nie są jeszcze obsługiwane, z wyjątkiem funzip (wtedy można wyodrębnić tylko pierwszego członka archiwum).

Możesz mieć trochę szczęścia z funzip.

David Pashley
źródło
Jeśli zip zawiera wiele plików, to -p może wydrukować pojedynczy plik, używając nazwy pliku jako parametru: rozpakuj -p temp.zip file-inside-zip
Taavi Ilves
7

To, co chcesz zrobić, to unzipwziąć plik ZIP na standardowe wejście zamiast argumentu. Zazwyczaj jest to łatwo wspierane przez gzipi tarrodzaj narzędzi z -argumentem. Ale standard unziptego nie robi (obsługuje jednak ekstrakcję do potoku). Jednak nie wszystko jest stracone...

Spójrz na stronę podręcznika funzip .

funzip bez argumentu pliku działa jak filtr; oznacza to, że zakłada, że ​​archiwum ZIP (lub plik spakowany gzipem) jest przesyłane do standardowego wejścia i wypakowuje pierwszy element z archiwum na standardowe wyjście. Gdy stdin pochodzi z urządzenia tty, funzip zakłada, że ​​nie może to być strumień skompresowanych danych (binarnych) i zamiast tego wyświetla krótki tekst pomocy. Jeśli istnieje argument pliku, dane wejściowe są odczytywane z określonego pliku zamiast ze standardowego wejścia.

Biorąc pod uwagę ograniczenie ekstrakcji z jednego elementu, funzip jest najbardziej przydatny w połączeniu z dodatkowym programem archiwizującym, takim jak tar (1). Poniższa sekcja zawiera przykład ilustrujący to użycie w przypadku kopii zapasowych dysków na taśmę.

Jest to zgodne z ideą, że większość archiwów linuksowych jest zwykle TAR'owana, a następnie w jakiś sposób skompresowana (gzip, bzip, i in.). To zadziała, jeśli masz tar.ZIP.


Warto zauważyć, że funzipzostał napisany przez oryginalnego autora Info-ZIP, Marka Adlera. Pisze na stronie man funzip,

this functionality should be incorporated into unzip itself (future release).

jednak nie ma takiej aktualizacji. Podejrzewam, że Mark uznał to za niepotrzebne, ponieważ inne metody archiwizacji działały łatwo z TAR.

nik
źródło
Tylko komentarz; niektórzy wolą Pythona lub dowolny język jako opcję rozpakowania. Najlepszym przykładem jest Heroku, który nie zawiera tar i rozpakuj w swoim systemie. Obejściem problemu jest użycie słoika przez zainstalowanie Javy, co jest dozwolone.
Nick
Więcej informacji na temat ograniczania funzip i podobnych narzędzi (w szczególności możliwości pokazania pierwszego członka archiwum) w tej odpowiedzi: unix.stackexchange.com/a/211286/77539
Joshua Goldberg
6

Lubię używać curl, ponieważ jest on instalowany domyślnie ( -Ljest potrzebny do często przekierowywanych):

curl -L http://example.com/file.zip | bsdtar -xvf - -C /path/to/directory/

Jednak bsdtarnie jest instalowany domyślnie i nie mogłem zabrać się funzipdo pracy.

Todd Partridge
źródło
Działa również dobrze z wieloma plikami
jonnor
5

Oto odpowiedź mojej odpowiedzi na podobne pytanie:

Format pliku ZIP zawiera katalog (indeks) na końcu archiwum. Ten katalog mówi, gdzie w archiwum znajduje się każdy plik, a zatem umożliwia szybki, losowy dostęp, bez odczytywania całego archiwum.

Wydaje się, że stanowi to problem przy próbie odczytania archiwum ZIP przez potok, ponieważ indeks nie jest dostępny do samego końca, a zatem poszczególne elementy nie mogą zostać poprawnie wyodrębnione, dopóki plik nie zostanie całkowicie odczytany i nie będzie już dostępny . Nic dziwnego, że większość dekompresorów ZIP po prostu zawodzi, gdy archiwum jest dostarczane przez potok.

Katalog na końcu archiwum nie jest jedynym miejscem, w którym meta informacje o pliku są przechowywane w archiwum. Ponadto poszczególne wpisy zawierają również te informacje w lokalnym nagłówku pliku w celu zapewnienia nadmiarowości.

Chociaż nie każdy dekompresor ZIP będzie używał lokalnych nagłówków plików, gdy indeks jest niedostępny, interfejsy tar i cpio do libarchive (aka bsdtar i bsdcpio) mogą i będą to robić podczas czytania przez potok, co oznacza, że ​​możliwe są:

wget -qO- http://example.org/file.zip | bsdtar -xvf-
ruario
źródło
4

Nie jest to możliwe w przypadku Info-Zip, który jest najczęstszą implementacją OSS. Co ważniejsze, nie jest to zalecane ze względu na konstrukcje archiwów ZIP.

Jeśli zmiana formatu jest dla Ciebie wykonalna, rozważ użycie tar (1). Jest całkiem zadowolony z przesyłanych strumieniowo danych wejściowych / wyjściowych i faktycznie oczekuje tego domyślnie.

Ponadto często można stwierdzić, czy aplikacje oczekują przesyłanych strumieniowo danych wejściowych / wyjściowych, określając „-” dla nazwy pliku. Info-Zip, jak można sobie wyobrazić, nie traktuje tego jako ważnego argumentu.

Dan Carley
źródło
4

W Zsh możesz wykonać następujące czynności:

unzip =( curl http://example.com/someZipFile.zip )
Ian Robertson
źródło
3

Najprostszym dostępnym narzędziem, które to zrobi, jest to jar, że zakłada się, że STDIN jest używany, jeśli przekaże się go bez argumentów pliku. Pobiera również argumenty podobne do tarprogramu dla operacji.

np. wypisuje zawartość archiwum

curl https://my.example.com/file.zip | jar t

Chociaż Java nie zawsze jest instalowana, na tych komputerach, na których się ona znajduje, jarjest zdecydowanie najwygodniejszą metodą.

Adrian
źródło
3

Repost mojej odpowiedzi :

BusyBox unzipmoże zająć standardowe wejście i wyodrębnić wszystkie pliki.

wget -qO- http://downloads.wordpress.org/plugin/akismet.2.5.3.zip | busybox unzip -

Myślnikiem po unzipjest użycie stdin jako danych wejściowych.

Możesz nawet,

cat file.zip | busybox unzip -

Ale to po prostu zbędne unzip file.zip.

Jeśli twoja dystrybucja domyślnie korzysta z BusyBox (np. Alpine), po prostu uruchom unzip -.

Saftever
źródło
1

Potrzebowałem czegoś bardziej złożonego - wyodrębnij konkretny plik, jeśli istnieje. Trudność polega na tym, że strumień pliku wejściowego może nie być plikiem zip, w którym to przypadku musiałem go kontynuować przez potok. Oto moje rozwiązanie (głównie dzięki rozwiązaniu Jasona R. Coombsa)

python -c "import zipfile,sys,StringIO
data=sys.stdin.read()
try:
    z=zipfile.ZipFile(StringIO.StringIO(data))
    z.open(\"$1\")
    sys.stdout.write(z.read(\"$1\"))
except (RuntimeError, zipfile.BadZipfile):
    sys.stdout.write(data)"

Zapisałem to jako plik o nazwie „effpoptp” (nie jest to prosta nazwa) w folderze „/ bin” na moim komputerze, więc testowanie wygląda tak:

cat defaultModel.mwb|effpoptp "document.mwb.xml"

Celem jest kontrola wersji plików MySQL Workbench, gdzie może to być plik xml o nazwie jako plik workbench lub pełny plik workbench.

SEoF
źródło