Inspecciona y extrae archivos ZIP con unzip
Usa unzip -l para inspeccionar un ZIP,
unzip -t para comprobar sus datos comprimidos y
unzip -n para extraerlos sin reemplazar archivos existentes.
Para un directorio de paquetes descargados, también necesitas un script por lotes que informe de los
fallos incluso cuando un archivo ZIP posterior se procese correctamente. Este tutorial te ofrece un
pequeño archivo ZIP de práctica y un script que mantiene cada paquete en su propio directorio de salida.
Comprueba tus herramientas
Usa Linux con Bash, Info-ZIP UnZip y las utilidades habituales cp,
mkdir, mktemp y rm.
También necesitas Info-ZIP Zip para crear el archivo ZIP de práctica. Estos ejemplos se probaron con
Bash 5.3.15, UnZip 6.00 y Zip 3.0; usa los paquetes que tu distribución mantiene actualizados.
Comprueba las implementaciones con:
bash --version && unzip -v && zip -v
El manual que se indica a continuación corresponde a Info-ZIP, por lo que otro programa llamado
unzip puede comportarse de manera diferente.
unzip -v también muestra UNZIP y
UNZIPOPT, variables de entorno que pueden añadir opciones predeterminadas.
Los comandos individuales que siguen suponen que esas variables no están definidas; el script por
lotes las elimina de su propio proceso.
Trabaja con archivos ZIP de confianza y sin cifrar, como paquetes de un distribuidor que hayas
verificado. -t comprueba los valores CRC almacenados en el archivo ZIP;
no autentica a su distribuidor ni demuestra que su contenido sea seguro. Un directorio de extracción
no es un entorno aislado para las subidas de usuarios. Los archivos ZIP que no sean de confianza
necesitan un proceso de trabajo aislado y límites de recursos que van más allá de este flujo de trabajo.
Inspecciona un ZIP antes de extraerlo
Pega lo siguiente en Bash para crear unzip-demo dentro de tu directorio actual.
Si ese nombre ya existe, la preparación se detiene sin cambiar su contenido. Los paréntesis mantienen
tu shell en su directorio original.
(
mkdir -- unzip-demo &&
cd -- unzip-demo &&
mkdir -- downloads &&
printf '{"mode":"development"}\n' > config.json &&
printf 'build complete\n' > build.log &&
zip -q ./downloads/package.zip config.json build.log
)
Desde el mismo directorio de inicio, enumera las entradas y luego comprueba sus datos:
unzip -l ./unzip-demo/downloads/package.zip &&
unzip -t ./unzip-demo/downloads/package.zip
La lista incluye config.json y build.log;
la prueba no debería informar de ningún error. Según el
manual de Info-ZIP, -l lee la lista,
mientras que -t descomprime las entradas en memoria y compara sus CRC.
Obtener la lista correctamente no demuestra por sí solo que los datos se puedan extraer.
Extrae ambas entradas en un directorio separado:
unzip -n ./unzip-demo/downloads/package.zip -d ./unzip-demo/extracted
Ahora deberías tener unzip-demo/extracted/config.json y unzip-demo/extracted/build.log.
-n omite un archivo existente sin pedir confirmación. Al volver a ejecutar
el comando, esto significa que un config.json editado localmente conserva los cambios
incluso si el comando devuelve cero. Usa -o solo cuando quieras reemplazar
archivos existentes; esta opción permite reemplazarlos sin pedir confirmación de sobrescritura.
Extrae solo lo que necesitas
Pasa un patrón de entradas entre comillas para seleccionar archivos JSON:
unzip -n ./unzip-demo/downloads/package.zip '*.json' -d ./unzip-demo/config-only
Esto crea config-only/config.json sin extraer build.log
y conserva los archivos existentes en ejecuciones posteriores. Las comillas impiden que Bash expanda
*.json según el contenido de tu directorio de trabajo; UnZip aplica el patrón
a las entradas del archivo ZIP. Para excluir entradas de registro que coincidan con un patrón, usa
-x '*.log'. Para inspeccionar solo la configuración sin crear un archivo, usa
unzip -p ./unzip-demo/downloads/package.zip config.json.
Procesa un directorio sin ocultar fallos
Para el procesamiento por lotes, usa una política de salida más estricta: si ya existe el destino de
un archivo ZIP, se considera un fallo, no una solicitud de combinar archivos. Guarda lo siguiente como
extract-zips.sh en tu directorio de inicio y luego ejecútalo con
bash, como se muestra a continuación. No lo cargues con source en tu shell.
El script procesa los archivos regulares no ocultos del nivel superior cuya extensión termina en
.zip en minúsculas; omite enlaces simbólicos y directorios. Mantén los archivos
de entrada sin cambios durante la ejecución y asigna el directorio raíz de salida a una sola
invocación a la vez. Necesita espacio para los archivos extraídos y una copia temporal del ZIP más
grande.
#!/usr/bin/env bash
if (( $# != 2 )) || [[ -z $1 || -z $2 ]]; then
printf 'Usage: bash extract-zips.sh INPUT_DIR OUTPUT_DIR\n' >&2
exit 2
fi
unset UNZIP UNZIPOPT
input_dir=$1
output_dir=$2
[[ $input_dir = /* ]] || input_dir="$PWD/$input_dir"
[[ $output_dir = /* ]] || output_dir="$PWD/$output_dir"
cd -P -- "$input_dir" || exit 1
input_dir=$PWD
mkdir -p -- "$output_dir" || exit 1
cd -P -- "$output_dir" || exit 1
output_dir=$PWD
scratch=$(mktemp -d -- "$output_dir/.unzip.XXXXXXXX") || exit 1
trap 'rm -rf -- "$scratch"' EXIT
shopt -s nullglob
status=0
count=0
for archive in "$input_dir"/*.zip; do
[[ -f "$archive" && ! -L "$archive" ]] || continue
count=$((count + 1))
name=${archive##*/}
destination="$output_dir/${name%.zip}"
if [[ -e "$destination" || -L "$destination" ]]; then
printf 'Destination already exists: %s\n' "$destination" >&2
status=1
continue
fi
printf 'Checking: %s\n' "$archive"
# UnZip expands archive-name wildcards even inside a quoted shell argument.
if ! cp -f -- "$archive" "$scratch/input.zip" ||
! unzip -tq -P '' "$scratch/input.zip" </dev/null; then
printf 'Cannot copy or validate: %s\n' "$archive" >&2
status=1
continue
fi
if ! mkdir -- "$destination"; then
status=1
continue
fi
if unzip -qo -P '' "$scratch/input.zip" -d "$destination" </dev/null; then
printf 'Extracted: %s\n' "$destination"
else
printf 'Extraction failed; inspect partial output: %s\n' "$destination" >&2
status=1
fi
done
if (( count == 0 )); then
printf 'No regular .zip files found in: %s\n' "$input_dir" >&2
status=1
fi
exit "$status"
Ejecuta el script guardado sobre el archivo ZIP de práctica:
bash ./extract-zips.sh ./unzip-demo/downloads ./unzip-demo/batch-output
Si la primera ejecución tiene éxito, imprime una línea Extracted: y crea
batch-output/package/config.json y batch-output/package/build.log.
Una segunda ejecución devuelve 1 y deja ese directorio intacto.
Elige un nuevo directorio raíz de salida cuando quieras realizar una extracción desde cero.
La copia temporal proporciona a UnZip el nombre literal input.zip.
Esto importa para nombres como release[1].zip: las comillas del shell por sí solas no
desactivan la búsqueda de coincidencias con comodines que UnZip aplica a los nombres de archivos ZIP.
El nombre de origen sigue determinando el destino. Las rutas se resuelven una vez con
cd -P y se obtienen de $PWD,
por lo que la detección y la extracción usan el mismo directorio físico sin perder los saltos de línea
finales en los nombres de directorio.
Cada ZIP debe superar -t antes de que se cree su destino.
Después, -o se aplica solo dentro de ese directorio recién creado, y
-P '' proporciona una contraseña vacía para que no se solicite una de forma
interactiva. Los paquetes protegidos con contraseña necesitan un flujo de trabajo independiente.
El script continúa procesando después de un fallo, pero nunca restablece
status a cero. Las
reglas de subshells y procesos
de Bash impiden que los cambios de directorio y las opciones del script guardado afecten al shell que
lo invoca.
Actúa según el resultado
El script por lotes devuelve 0 solo cuando encuentra al menos un archivo
ZIP apto y todos los archivos ZIP se procesan sin advertencias ni errores. Devuelve
1 si ya existe un destino, no hay entradas aptas o se produce un fallo de
copia, validación o extracción; 2 significa que los argumentos son
incorrectos. Un resultado satisfactorio posterior no puede ocultar un fallo anterior.
Conserva los diagnósticos de UnZip al investigar un fallo. Un estado de UnZip distinto de cero puede indicar datos dañados, compresión o cifrado no compatibles, o un problema del sistema de archivos. Llamar «corrupción» a todos estos casos te llevaría a buscar en el lugar equivocado.
Si falla la comprobación de datos, no se crea ningún destino para ese archivo ZIP. Un fallo durante la extracción puede dejar archivos parciales; inspecciónalos antes de reintentar la extracción en un nuevo directorio raíz de salida. Las salidas completadas anteriormente y los ZIP originales permanecen en su lugar. El script elimina únicamente su copia temporal privada.
Para una tarea de compilación, deja que el estado distinto de cero del script detenga el procesamiento posterior. Para volver a ejecutarlo manualmente, elige un nuevo directorio raíz de salida o inspecciona primero los archivos existentes. Esa decisión corresponde a tu flujo de trabajo, no a una solicitud de confirmación de sobrescritura sin nadie que la atienda.
