Entspricht Tars "--strip-components = 1" beim Entpacken?

49

Ich habe ein Skript, das eine tar.gz-Datei in ein bestimmtes Unterverzeichnis mysubfolder extrahiert :

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

Gibt es eine äquivalente Möglichkeit, dies mit einer Zip-Datei zu tun?

frigg
quelle
2
Benutze einfach bsdtar
drizzt 10.08.15

Antworten:

26

Wie Mathias sagte, unzipgibt es keine solche Option, aber ein einzeiliges Bash-Skript kann den Job erledigen.

Das Problem ist: Der beste Ansatz hängt von Ihrem Archivlayout ab. Eine Lösung , die übernimmt ein einziges Top-Level - dir kläglich scheitert , wenn der Inhalt direkt in das Archiv Wurzel ist (man denke etwa /a/foo /b/foo /foound das Chaos der Stripping /aund /b).

Und das gleiche Scheitern passiert mit tar --strip-component. Es gibt keine Einheitslösung.

Also, um das Wurzelverzeichnis zu entfernen, vorausgesetzt, es gibt eins (und nur eins):

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

Stellen Sie einfach sicher, dass Dateien / Verzeichnisse der zweiten Ebene nicht den gleichen Namen wie die übergeordneten Dateien der obersten Ebene haben (z. B. /foo/foo). Aber /foo/bar/foound /foo/bar/barsind ok Wenn dies der Fall ist oder Sie nur auf Nummer sicher gehen möchten, können Sie ein temporäres Verzeichnis zum Extrahieren verwenden:

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

Wenn Sie Bash verwenden, können Sie testen, ob die oberste Ebene ein einzelnes Verzeichnis ist oder nicht:

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

Apropos Bash, Sie sollten es aktivieren dotglob, um versteckte Dateien einzuschließen, und Sie können alles in einer einzigen, praktischen Funktion zusammenfassen:

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

Geben Sie das jetzt in Ihr ~/.bashrcoder ein, ~/.profileund Sie müssen sich nie wieder darum kümmern. Verwenden Sie einfach als:

unzip-strip sourcefile.zip mysubfolder

(Beachten Sie, dass es automatisch mysubfolderfür Sie erstellt wird, wenn es nicht existiert.)

MestreLion
quelle
Dies wird nicht in eine bestehende Verzeichnisstruktur entpackt, wie ich gehofft hatte (ich habe versucht,. Anstelle von mysubfolder zu verwenden). Am Ende habe ich nur entpackt (zip-with-top-dir.zip) und dann kopiert (cp -rv extrahiert-top-zip-dir / *.).
Catgofire
4

Ich habe eine solche Option in den Manualseiten für nicht gefundenunzip , daher fürchte ich, dass dies unmöglich ist. :(

Sie können dies jedoch (je nach Situation) umgehen. Wenn Sie beispielsweise sicher sind, dass das einzige Verzeichnis der obersten Ebene in der ZIP-Datei mit dem Namen foo-gefolgt von einer Versionsnummer versehen ist, können Sie Folgendes tun:

cd /tmp
unzip /path/to/file.zip
cd foo-*
cp -r . /path/to/destination/folder
Mathias Bynens
quelle
Nizza Ansatz, aber ein bisschen unvollständig: Sie haben noch foo * dir mit dem vollständigen Inhalt extrahiert.
MestreLion
Ja, ich habe nicht rm -rf foo-*absichtlich hinzugefügt , da dies möglicherweise gefährlich ist. Was ist, wenn es bereits einen Ordner mit dem Namen gab foo-bar? Beachten Sie, dass die Extraktion in dem /tmpOrdner durchgeführt wird, der von Zeit zu Zeit automatisch geleert wird.
Mathias Bynens
Aus diesem Grund habe ich Vorgänge verkettet mit &&: Ein bestimmter Schritt wird nur ausgeführt, wenn der vorherige erfolgreich war. Der letzte Schritt wird also rmnur ausgeführt, wenn alle Schritte ohne Fehler abgeschlossen wurden.
MestreLion
2
Das ist auch der Grund, warum man niemals /tmp/some-hardcoded-folder-nameeinen temporären Ordner verwenden sollte, sondern stattdessen mktemp: Es ist garantiert, dass es keinen solchen Ordner gibt. Überprüfen Sie meine Antwort unten.
MestreLion
1

Sie können verwenden, -jum Pfade zu verschrotten (keine Verzeichnisse zu erstellen). Dies wird nur für einige gängige einstufige Archive empfohlen. Archive mit mehrstufigen Verzeichnisstrukturen werden reduziert - dies kann sogar zu Namenskonflikten für die zu extrahierenden Dateien führen.

Aus der Manpage von unzip:

   -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
quelle