Odpowiednik „--strip-components = 1” tar w rozpakowaniu?

48

Mam skrypt, który wyodrębnia plik tar.gz do określonego podkatalogu mysubfolder :

mkdir mysubfolder; tar --extract --file=sourcefile.tar.gz --strip-components=1 --directory=mysubfolder;

Czy istnieje równoważny sposób na zrobienie tego za pomocą pliku zip?

cholerstwo
źródło
2
Wystarczy użyć bsdtar
drizzt

Odpowiedzi:

26

Jak powiedział Mathias, unzipnie ma takiej opcji, ale skrypt bash z jedną linią może to zrobić.

Problem polega na tym: najlepsze podejście zależy od układu archiwum. Rozwiązanie, które zakłada, że jeden katalog najwyższego poziomu zawiedzie, jeśli zawartość znajdzie się bezpośrednio w katalogu głównym archiwum (pomyśl o /a/foo /b/foo /foochaosie usuwania /ai /b).

To samo dzieje się z tar --strip-component. Nie ma jednego uniwersalnego rozwiązania.

Tak więc, aby usunąć katalog główny, zakładając, że jest jeden (i tylko jeden):

unzip -d "$dest" "$zip" && f=("$dest"/*) && mv "$dest"/*/* "$dest" && rmdir "${f[@]}"

Upewnij się tylko, że pliki / katalogi drugiego poziomu nie mają tej samej nazwy rodzica najwyższego poziomu (na przykład /foo/foo). Ale /foo/bar/fooi /foo/bar/barsą w porządku. Jeśli tak, lub po prostu chcesz być bezpieczny, możesz użyć temp temp do ekstrakcji:

temp=$(mktemp -d) && unzip -d "$temp" "$zip" && mkdir -p "$dest" &&
mv "$temp"/*/* "$dest" && rmdir "$temp"/* "$temp"

Jeśli używasz Bash, możesz sprawdzić, czy najwyższy poziom to pojedynczy katalog, czy nie używasz:

f=("$temp"/*); (( ${#f[@]} == 1 )) && [[ -d "${f[0]}" ]] && echo "Single dir!"

Mówiąc o Bash, należy włączyć, dotglobaby dołączyć ukryte pliki, a wszystko można zawinąć w jedną, przydatną funkcję:

unzip-strip() (
    local zip=$1
    local dest=${2:-.}
    local temp=$(mktemp -d) && unzip -d "$temp" "$zip" && mkdir -p "$dest" &&
    shopt -s dotglob && local f=("$temp"/*) &&
    if (( ${#f[@]} == 1 )) && [[ -d "${f[0]}" ]] ; then
        mv "$temp"/*/* "$dest"
    else
        mv "$temp"/* "$dest"
    fi && rmdir "$temp"/* "$temp"
)

Teraz włóż to do swojego ~/.bashrclub ~/.profilejuż nigdy nie będziesz się o to martwić. Po prostu użyj jako:

unzip-strip sourcefile.zip mysubfolder

(zauważ, że utworzy się automatycznie mysubfolder, jeśli nie istnieje)

MestreLion
źródło
Nie rozpakuje się to do istniejącej struktury katalogów, jak się spodziewałem (próbowałem użyć. Zamiast mysubfolder). Skończyło się na rozpakowaniu (rozpakowanie zip -with-top-dir.zip), a następnie skopiowaniu (cp -rv extract-top-zip-dir / *.).
catgofire,
4

Nie mogłem znaleźć takiej opcji na stronach podręcznika dlaunzip , więc obawiam się, że to niemożliwe. :(

Jednak (w zależności od sytuacji) można obejść ten problem. Na przykład, jeśli masz pewność, że nazwa jedynego katalogu najwyższego poziomu w pliku zip, foo-po którym następuje numer wersji, możesz zrobić coś takiego:

cd /tmp
unzip /path/to/file.zip
cd foo-*
cp -r . /path/to/destination/folder
Mathias Bynens
źródło
Ładne podejście, ale nieco niekompletne: nadal będziesz mieć foo * dir z pełną rozpakowaną zawartością.
MestreLion
Tak, nie dodałem rm -rf foo-*celowo, ponieważ jest to potencjalnie niebezpieczne. Co jeśli już istnieje folder o nazwie foo-bar? Pamiętaj, że rozpakowywanie odbywa się w /tmpfolderze, który co pewien czas jest automatycznie opróżniany.
Mathias Bynens
Dlatego połączyłem operacje za pomocą &&: dany krok ma miejsce tylko wtedy, gdy poprzedni krok się powiódł, więc ostatni (a rm) działa tylko wtedy, gdy wszystkie kroki zostały zakończone bez błędu.
MestreLion
2
Dlatego też nigdy /tmp/some-hardcoded-folder-namenie należy używać użytkownika jako folderu tymczasowego, ale zamiast tego należy mktempdo tego użyć : gwarantuje to, że nie będzie takiego istniejącego folderu. Sprawdź moją odpowiedź poniżej.
MestreLion
1

Możesz użyć -jdo śmieciowych ścieżek (nie twórz katalogów). Jest to zalecane tylko w przypadku dość powszechnych archiwów jednopoziomowych. Archiwa z wielopoziomowymi strukturami katalogowymi zostaną spłaszczone - może to nawet prowadzić do kolizji nazw plików w celu wyodrębnienia.

Ze strony man rozpakowania:

   -j     junk  paths.   The  archive's directory structure is not recreated; all files are deposited in the
          extraction directory (by default, the current one).
Pedro Rodrigues
źródło