Każdemu zdarza się czasami posiadać wiele archiwów ZIP, czy RAR, które trzeba rozpakować, jednak robienie tego ręcznie na kilkudziesięciu, czy kilkuset plikach może być nieco męczące. Zadanie to ułatwić nam może bash.
Aby rozpakować wszystkie pliki z danego katalogu, wyświetlimy najpierw ich listę, za pomocą konsolowego polecenia ls, zapiszemy ją jako tablicę i na każdym z jej elementów wykonamy komendę, służącą do rozpakowywania z automatycznym nadpisywaniem (abyśmy nie musieli cały czas kontrolować ekranu). Poniżej prosty skrypt, który obrazuje powyższe założenia:
#!/bin/bash
IFS=’
‘
PLIKI=`ls -1 *.zip *.zip` # listuje pliki zip i ZIP wyświetlając wyniki w osobnych liniach
for i in $PLIKI # pętla dla każdego elementu w tablicy
do
echo $i # wyświetlenie elementu
unzip -o ./$i # rozpakowanie pliku z nadpisaniem
done
1 metoda alternatywna (zasugerował Marcin & P. Hosowicz):
#!/bin/bash
for x in *.[zZ][iI][pP]
do
echo $x
unzip -o ./$x
done
2 metoda alternatywna (zasugerował P. Hosowicz):
ls -1 *.[zZ][iI][pP] | xargs -l unzip -o
3 metoda alternatywna (zasugerował M. Górny):
find -maxdepth 1 -name ‘*.[zZ][iI][pP]‘ -exec unzip -o ‘{}’ ‘;’
Od każdego wymagana jest także rzetelność, kultura słowa oraz szacunek do pozostałych dyskutantów. Nie akceptowane są próby manipulacji, uciekanie się do emocji, wulgarne wypowiedzi oraz argumentacja pozamerytoryczna. Jeśli nie jesteś w stanie zastosować się do powyższych zasad, Twój komentarz prawdopodobnie zostanie usunięty.
Opracowano na podstawie: "Etyka międzyludzkiej komunikacji", red. J. Puzynina, wyd. Semper, Warszawa 1993
Kanał RSS z komentarzami do tego wpisu. TrackBack URL
Hmm? A nie lepiej skorzystać z pętli for po plikach zamiast ls i pętli for?
for x in *zip
do
echo $x
unzip -o ./$x
done
Edit: Ech, nie ma Textile
Marcin "Ktos" — 12 maja 2007 @ 12:26
Można i tak
a załapie to i *.ZIP i *zip?
dunDer — 12 maja 2007 @ 12:30
Nie wiem, chyba nie. Trzeba sprawdzić
Marcin "Ktos" — 12 maja 2007 @ 12:34
a czemu nie unzip -o *.zip ?
ja — 12 maja 2007 @ 13:15
@dunDer: po pierwsze primo pętla jaką podałeś jest nieteges, warto się odzwyczaić od konstrukcji:
for X in $(ls -1 *.cos) ; do
…
done
… ponieważ to nie będzie działać zgodnie z założeniami dla plików ze znakiem nowej linii w nazwie. Jeśli operacją będzie rm, wsadzisz to w jakimś skrypcie uruchamianym automatycznie, a ktoś będzie złośliwy to może stworzyć plik cenny-plik.txtnjakis-plik.txt i doprowadzic do skasowania pliku cenny-plik.txt. Zamiast tego najlepiej korzystać z shellowego rozwijania nazw i wtedy :
for FILE in *.[zZ][iI][pP] ; do
unzip -o $FILE
done
Dodatkowo – w takiej formie jak podałeś, to się będzie wyp* na plikach ze spacją w nazwie, bo domyślnie zmienna IFS (man bash) zawiera spację, tabulator i znak nowej linii. Żeby to obejść, musiałbyś przed tętlą ustawić IFS, np. tak:
IFS=’
‘
(pi*oko: musi to być napisane dokłądnie od nowej linii)
Ponadto po drugie primo – wystarczy :
unzip *.[zZ][iI][pP]
… i tyle.
Piotr Hosowicz — 12 maja 2007 @ 13:32
1. Kto tworzy pliki ze znakiem nowej linii w nazwie / udostępnia je w necie? …

2. To ostatnie nie zadziała dla plików z kropkami w nazwie
3. Sprawdziłem i spacja w nazwie rzeczywiście jest problemem – poprawiłem dodając IFS
dunDer — 12 maja 2007 @ 13:43
@dunDer:
ad 1: przecież Ci właśnie napisałem.
ad 2: zadziała, sprawdź.
Piotr Hosowicz — 12 maja 2007 @ 13:46
> Jeśli operacją będzie rm, wsadzisz to w jakimś skrypcie uruchamianym automatycznie, a ktoś będzie złośliwy to może stworzyć plik cenny-plik.txtnjakis-plik.txt i doprowadzic do skasowania pliku cenny-plik.txt
Sprawdziłem i nie działa to tak jak mówisz za bardzo – utworzyłem plik o nazwie jakiej podałeś i on został usunięty poprzez skrypt, nie mówiąc już o tym że jeśli sprawdzamy *.txt to i tak obydwa z założenia będą usunięte, więc żadna to różnica. Nawet jeśli wpisze się inne rozszerzenie to i tak usunięty zostaje plik o takiej pełnej nazwie, a nie ten który miałby zostać złośliwie usunięty, bo przed chwilą sprawdziłem.
Ad.2 Sprawdziłem: caution: filename not matched: x.y.z.zip – nie działa.
dunDer — 12 maja 2007 @ 13:55
Ad 1: zwracasz uwagę zupełnie nei na to, o co tu chodzi – to że obydwa człony miały w sobie rozszerzenie txt to był tylko, przykład, może nie najlepszy. Chodziło mi o sytuację taką:
$ ls -1
abcd.txt
abcd.txt?efgh.zip
script.sh
$ cat script.sh
#!/bin/bash
IFS=’
‘
for FILE in $(ls -1 *.zip); do
ls -1 $FILE
done
$ ./script.sh
abcd.txt
ls: efgh.zip: Nie ma takiego pliku ani katalogu
Jesli miałbyś tam usuwanie, a nie listowanie, to usunięty byłby cenny plik abcd.txt, jeśli ktoś by tam zlośliwie wrzucił plik abcd.txtncostam.zip (na listingu znak ? to nowa linia).
Ad 2: nie wiem czy my mówmy o tym samym, u mnie jest tak:
$ ls -1
abcd efgh.zip
abcd.efgh.zIP
abcd.zip
abcd.ZIP
camel.ZiP
script.sh
$ ls -1 *.[zZ][iI][pP]
abcd efgh.zip
abcd.efgh.zIP
abcd.zip
abcd.ZIP
camel.ZiP
Czyli SOA#1, działa.
Piotr Hosowicz — 12 maja 2007 @ 14:06
Ad2. chodziło mi o "unzip *.[zZ][iI][pP]" jak pisałeś na końcu tamtego komentarza
Ad1. Rzeczywiście, ale jeśli chodzi o skrypt do rozpakowywania to raczej nie jest to żadnym zagrożeniem tym bardziej jeśli piszemy go sami i wykonujemy z reguły tylko wtedy kiedy potrzeba. W każdym razie słuszna uwaga i dzięki za uzupełnienie z komentarzach
dunDer — 12 maja 2007 @ 14:23
Ad 2: dunDer, pokaż dokładnie co robisz. Czy to będzie unzip czy ls to nie powinno stanowić żadnej różnicy, bo plikowe symbole wieloznaczne są rozwijane nie przez program ls czy unzip, tylko przez shella. Pokaż dokładnie jakie masz pliki testowe w katalogu i jakie polecenie daje jakie efekty – tak jak ja Ci podałem zrzuty z konsoli.
Piotr Hosowicz — 12 maja 2007 @ 14:36
Panowie, o ile pamiętam, to tylko pierwszy argument unzip określał archiwum, pozostałe zaś nazwy plików do wypakowania z niego.
Nie lepiej czasem:
find -name ‘*.[zZ][iI][pP]‘ -exec echo ‘{}’; unzip -o ‘{}’ ‘;’
?
Michał Górny — 12 maja 2007 @ 14:39
@Michał Górny: masz rację, sprawdziłem w manualu, jest jak piszesz, te dalsze elementy na liśćie to dla unzip’a pliki do wyciągnięcia z ZIPa, no i tam takich nie ma, więc wypisuje błąd.
Piotr Hosowicz — 12 maja 2007 @ 14:42
… zatem pętla jakby nie było jest jakaś potrzebna, a co do tego czy nie lepiej:
adam@adam:~$ ls *.zip
x.y.z.zip
adam@adam:~$ find -name ‘*.[zZ][iI][pP]‘ -exec echo ‘{}’; unzip -o ‘{}’ ‘;’
find: brak argumentu dla `-exec’
unzip: cannot find or open {}, {}.zip or {}.ZIP.
?
dunDer — 12 maja 2007 @ 14:44
Nie, obejdzie się bez pętli, po prostu do unzip musi być podawany na każde jego uruchomienie tylko jeden argument, będący nazwą pliku ZIP, tak więc można tak:
$ ls -1 *.[zZ][iI][pP] | xargs -l unzip -o
-l (Ly-małe) to to samo co –max-lines , jak się go poda bez podawania ile to ma być tych linii to domyślną wartością jest 1 (jeden).
Piotr Hosowicz — 12 maja 2007 @ 14:52
@dunDer: Mały błąd w moim, średnik mu przeszkadza.
find -name ‘*.[zZ][iI][pP]‘ -exec sh -c "echo ‘{}’; unzip -o ‘{}’" ‘;’
Chyba że bez wypisywania nazw, to prościej:
find -name ‘*.[zZ][iI][pP]‘ -exec unzip -o ‘{}’ ‘;’
Michał Górny — 12 maja 2007 @ 14:56
@mgorny: tylko problem jest tego typu że Twój skrypt będzie wypakowywał też wszystko z podkatalogów rekursywnie
dunDer — 12 maja 2007 @ 15:04
@dunDer: to można załatwić opcją find’a o nazwie -maxdepth, konkretnioej -maxdepth 1 .
Piotr Hosowicz — 12 maja 2007 @ 15:06
swoją drogą to ciekawe iloma różnymi metodami można zrobić to samo
dunDer — 12 maja 2007 @ 15:07
Wrzuć do notki te alternatywne metody q ;.
Michał Górny — 12 maja 2007 @ 15:43
dzięki, informacje na tej stronie byly dla mnie pomocne
slawek — 2 czerwca 2009 @ 11:17