77 votes

Équivalent de l'option "--strip-components=1" de tar dans unzip ?

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

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

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

36voto

MestreLion Points 2107

Comme l'a dit Mathias, unzip n'a pas une telle option, mais un bash script en une ligne peut faire le travail.

Le problème est que la meilleure approche dépend de la disposition de vos archives. Une solution qui suppose que un seul répertoire de premier niveau échouera lamentablement si le contenu se trouve directement à la racine de l'archive (pensez à /a/foo /b/foo /foo et le chaos du déshabillage /a y /b ).

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

Donc, pour dépouiller le répertoire racine, en supposant qu'il y a est un (et seulement un) :

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

Assurez-vous simplement que les fichiers/répertoires de second niveau no ont le même nom que le parent de premier niveau (par exemple, /foo/foo ). Mais /foo/bar/foo y /foo/bar/bar sont bien. Si c'est le cas, ou si vous voulez juste être sûr, vous pouvez utiliser un disque provisoire 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 répertoire unique ou non en utilisant :

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

En parlant de Bash, vous devriez activer dotglob pour inclure les fichiers cachés, et vous pouvez tout regrouper dans une seule et même fonction très pratique :

# unzip featuring an enhanced version of tar's --strip-components=1
# Usage: unzip-strip ARCHIVE [DESTDIR] [EXTRA_cp_OPTIONS]
# Derive DESTDIR to current dir and archive filename or toplevel dir
unzip-strip() (
    set -eu
    local archive=$1
    local destdir=${2:-}
    shift; shift || :
    local tmpdir=$(mktemp -d)
    trap 'rm -rf -- "$tmpdir"' EXIT
    unzip -qd "$tmpdir" -- "$archive"
    shopt -s dotglob
    local files=("$tmpdir"/*) name i=1
    if (( ${#files[@]} == 1 )) && [[ -d "${files[0]}" ]]; then
        name=$(basename "${files[0]}")
        files=("$tmpdir"/*/*)
    else
        name=$(basename "$archive"); name=${archive%.*}
        files=("$tmpdir"/*)
    fi
    if [[ -z "$destdir" ]]; then
        destdir=./"$name"
    fi
    while [[ -f "$destdir" ]]; do destdir=${destdir}-$((i++)); done
    mkdir -p "$destdir"
    cp -ar "$@" -t "$destdir" -- "${files[@]}"
)

Maintenant, mettez ça dans votre ~/.bashrc et vous n'aurez plus jamais à vous en soucier. A utiliser simplement comme :

unzip-strip sourcefile.zip [mysubfolder] [OPTIONS]

Cette petite bête le fera :

  • Créer mysubfolder pour vous s'il n'existe pas
  • Détectez automatiquement si votre archive zip contient un seul répertoire de premier niveau et gérez l'extraction en conséquence.
  • mysubfolder es en option . S'il est vide, il sera extrait dans un sous-répertoire du répertoire actuel (pas nécessairement le répertoire archives répertoire !), dont le nom a été donné :
    • Le seul répertoire de premier niveau dans l'archive, s'il y en a un.
    • ou le nom du fichier d'archive, sans le (vraisemblablement .zip ) extension
  • Si le chemin de destination, donné ou dérivé, existe déjà en tant que fichier incrémente le nom jusqu'à ce qu'un nom approprié soit trouvé (nouveau chemin ou répertoire existant).
  • Par défaut :
    • être silencieux
    • écraser tout fichier existant
    • préserver les liens et les attributs (mode, horodatage, etc.)
  • Pass extra OPTIONS a cp . Les options utiles sont :
    • -v|--verbose : sortir chaque fichier copié, tout comme unzip fait
    • -n|--no-clobber : ne pas écraser les fichiers existants
    • -u|--update : n'écrase que les fichiers qui sont plus récents que la destination
  • Exiger un supplément OPTIONS à partir du 3ème argument. Créez un analyseur d'arguments approprié si nécessaire.
  • Utilisez double l'espace disque extrait pendant l'opération, en raison de l'approche "extraire dans le répertoire temporaire et copier". Il n'y a aucun moyen de contourner ce problème sans perdre une partie de sa flexibilité et de ses fonctionnalités.

12voto

Pedro Rodrigues Points 229

Vous pouvez utiliser -j pour créer des chemins d'accès inutiles (ne pas créer de répertoires). Ceci n'est recommandé que pour les archives à un seul niveau les plus courantes. Les archives avec des structures de répertoire à plusieurs niveaux seront aplaties - cela peut même conduire à des conflits de noms pour les fichiers à extraire.

Extrait 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).

5voto

Skrud Points 2335

Je n'ai pas trouvé une telle option dans les pages du manuel pour unzip donc j'ai peur que ce soit impossible. :(

Toutefois, (en fonction de la situation), vous pouvez contourner ce problème. Par exemple, si vous êtes sûr que le seul répertoire de premier niveau du fichier zip s'appelle foo- suivi d'un numéro de version, vous pourriez faire quelque chose comme ceci :

cd /tmp
unzip /path/to/file.zip
cd foo-*
cp -r . /path/to/destination/folder

3voto

barnabas Points 11

Comme d'autres l'ont noté, unzip ne prend pas en charge cette fonction. Cependant, bsdtar peut également extraire les fichiers zip.

bsdtar xvf app.zip --strip-components=1 -C /opt/some-app

bsdtar peut également extraire des flux (ce qu'unzip ne supporte pas).

curl -sSL https://... | bsdtar xvf - --strip-components=1 -C /opt/some-app

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X