Équivalent au «--strip-components = 1» de tar dans unzip?

49

J'ai un script qui extrait un fichier tar.gz dans un sous-répertoire mysubfolder spécifié :

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

Y a-t-il un moyen équivalent de faire cela avec un fichier zip?

Frigg
la source
2
Il suffit d'utiliser bsdtar
drizzt

Réponses:

26

Comme Mathias l'a dit, unzipcette option n'existe pas, mais un script bash à une ligne peut faire l'affaire.

Le problème est que la meilleure approche dépend de la disposition de vos archives. Une solution supposant un seul répertoire de niveau supérieur échouera misérablement si le contenu se trouve directement dans la racine de l’archive (pensez /a/foo /b/foo /fooau chaos de la suppression /aet de la suppression /b).

Et le même échec se produit avec tar --strip-component. Il n'y a pas de solution unique.

Donc, pour effacer le répertoire racine, en supposant qu’il en existe un (et un seul ):

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

Assurez-vous simplement que les fichiers / répertoires de deuxième niveau ne portent pas le même nom que le parent de niveau supérieur (par exemple, /foo/foo). Mais /foo/bar/fooet /foo/bar/barça va. S'ils le font, ou si vous voulez simplement être en sécurité, vous pouvez utiliser un répertoire temporaire pour l'extraction:

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

Si vous utilisez Bash, vous pouvez tester si le niveau supérieur est un seul répertoire ou non:

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

En parlant de Bash, vous devez activer dotglobpour inclure les fichiers cachés, et vous pouvez tout encapsuler dans une seule fonction pratique:

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

Maintenant, mettez cela dans votre ~/.bashrcou ~/.profileet vous n'aurez plus jamais à vous en soucier. Utilisez simplement comme:

unzip-strip sourcefile.zip mysubfolder

(notez qu'il sera automatiquement créé mysubfolderpour vous s'il n'existe pas)

MestreLion
la source
Cela ne décompresserait pas dans une structure de répertoires existante comme je l'avais espéré (j'ai essayé d'utiliser. À la place de mysubfolder). J'ai fini par décompresser (décompresser zip-with-top-dir.zip) puis copier (cp-rv extra-top-zip-dir / *.).
Catgofire
4

Je ne pouvais pas trouver une telle option dans les pages de manuelunzip , je crains donc que ce soit impossible. :(

Cependant, (selon la situation), vous pouvez contourner le problème. Par exemple, si vous êtes certain que le seul répertoire de niveau supérieur du fichier zip est nommé, foo-suivi d'un numéro de version, vous pouvez procéder comme suit:

cd /tmp
unzip /path/to/file.zip
cd foo-*
cp -r . /path/to/destination/folder
Mathias Bynens
la source
Belle approche, mais un peu incomplète: vous aurez toujours foo * dir avec le contenu extrait complet.
MestreLion
Oui, je n'ai pas ajouté rm -rf foo-*exprès car c'est potentiellement dangereux. Et s'il y avait déjà un dossier nommé foo-bar? Notez que l'extraction est effectuée dans le /tmpdossier, qui se vide automatiquement de temps en temps.
Mathias Bynens
C'est pourquoi j'ai chaîné les opérations en utilisant &&: une étape donnée ne se produit que si l'étape précédente a réussi, donc la dernière (la rm) ne s'exécute que si toutes les étapes sont terminées sans erreur.
MestreLion
2
C’est aussi pourquoi il ne faut jamais utiliser l’utilisateur /tmp/some-hardcoded-folder-namecomme dossier temporaire, mais plutôt utiliser mktemppour cela: cela garantit qu’il n’y aura pas un tel dossier existant. Vérifiez ma réponse ci-dessous.
MestreLion
1

Vous pouvez utiliser -jpour les chemins indésirables (ne pas créer de répertoires). Ceci n'est recommandé que pour les archives à un seul niveau quelque peu communes. Les archives avec des structures de répertoire à plusieurs niveaux seront aplaties - cela pourrait même conduire à des conflits de noms pour les fichiers à extraire.

De la page de manuel de 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
la source