¿Cómo "extraer" un archivo zip?

52

Extraje un archivo zip en una carpeta no vacía. El archivo zip tiene muchos archivos y una jerarquía profunda, que se fusionó con el árbol existente del directorio de destino. ¿Cómo puedo eliminar los archivos y directorios que se crearon descomprimiendo sin destruir los archivos y directorios que ya estaban allí? Por supuesto, todavía tengo el archivo zip en el que me fusioné, por lo que la información está ahí.

mafp
fuente
Umm gracias por aceptar, pero fue realmente idea de @ jjin. No estaba al tanto de las lqopciones para unzizp, solo agregué algunos trucos clásicos * nix alrededor de su respuesta principal.
terdon
Está bien, realmente no me importa tanto. Agregué mi propia versión diferente de manejo de espacios en blanco de todos modos.
jjlin
@terdon Sí ... también voté por la respuesta de jjlin, pero solo puedo aceptar una respuesta.
mafp
Para referencia futura, siempre realice una de las siguientes acciones con un archivo desconocido de cualquier formato: 1) Extraiga a un directorio vacío o 2) Enliste primero (descomprima -l) antes de extraerlo para que pueda ver si es desagradable como este. Los archivos creados sin un directorio de nivel superior con todo lo que está debajo son de mala forma. Cuando se hace con alquitrán, en realidad se llaman bombas de alquitrán, por lo que supongo que esto podría llamarse una bomba zip.
Joe
@ Joe tiene sus usos. Los paquetes LaTeX, por ejemplo, pueden venir en foo.tds.zipforma. Estas cremalleras se fusionan en un árbol TEXMF, lo cual es muy conveniente. Pero si alguna vez desea eliminar dicho paquete, se enfrenta al problema que describí.
mafp

Respuestas:

28

La respuesta de jjlin es el camino a seguir. Solo quiero agregar algunas opciones para directorios:

  • Eliminar todos los archivos extraídos , sin directorios :

    unzip -lqq file.zip | gawk -F"  " '{print $NF;}' |
      while IFS= read -r n; do rm "$n"; done
  • Eliminar solo archivos extraídos y directorios vacíos

    unzip -lqq file.zip | gawk -F"  " '{print $NF;}' |
      while IFS= read -r n; do rm "$n"; done; rmdir *

    Sin opciones, rmdirelimina solo directorios vacíos, dejará solo archivos y carpetas no vacías para que pueda ejecutarlo de manera segura *.

  • Elimine todo lo extraído, pero solicite una confirmación antes de cada eliminación:

    unzip -lqq file.zip | gawk -F"  " '{print $NF;}' |
      while IFS= read -r n; do rm -ri "$n"; done; rmdir *

    La -ibandera hará rmque aparezca un mensaje antes de cada eliminación, puede elegir Sí o No.

  • Eliminar todo lo extraído, directorios incluidos:

    unzip -lqq file.zip | gawk -F"  " '{print $NF;}' |
      while IFS= read -r n; do rm -rf "$n"; done
terdon
fuente
Eliminar directorios vacíos se realiza fácilmente con find: find * -depth -type d -exec rmdir {} +e ignorar todos los Directory not emptymensajes. Puede ser legal acortar esto a find * -type d -deletemedida que se -deleteactiva la opción, -depthpero no he verificado que -deleteno elimine un directorio no vacío.
Adrian Pronk
@AdrianPronk no:find: cannot delete './foo': Directory not empty
terdon
28

Puede usar unzip -lqq <filename.zip>para enumerar el contenido del archivo zip; sin embargo, esto incluirá información extraña que necesitarás filtrar. Aquí hay un comando que funciona para mí:

unzip -lqq file.zip | awk '{print $4;}' | xargs rm -rf

El awkcomando extrae solo los nombres de los archivos y directorios. Luego, el resultado se pasa xargsa eliminar todo. Sugiero hacer una ejecución en seco del comando (es decir, omitiendo la xargs rm -rfparte) primero para asegurarse de que los resultados sean correctos.

El comando anterior tendrá problemas relacionados con las rutas que tienen espacios en blanco. Esta versión (más complicada) debería arreglar eso:

unzip -lqq file.zip | awk '{$1=$2=$3=""; sub(/ */, "", $0); printf "%s%s", $0, "\0"}' | xargs -0 rm -rf
jjlin
fuente
Esto ya está bastante cerca de lo que tenía en mente, pero unzip -lqqtambién enumera los directorios contenidos en el archivo zip. Por ahora, dejaría todos los directorios solos. Cómo eliminar todos los directorios vacíos en un árbol podría ser una pregunta de seguimiento.
mafp
@mafp Ese es un buen punto sobre los directorios. Puede agregar grep -v '/$'a la canalización para omitir la eliminación de los directorios (que tienen una barra diagonal final, AFAICT).
jjlin
@terdon En realidad, creo que el problema comienza en el awk, ya que imprimir solo $ 4 no imprimirá la ruta completa.
jjlin
No creo que deba usar la -ropción de rm: parece estar pidiendo problemas, especialmente cuando se combina con la -fopción. No usaría la -fopción en absoluto en este escenario.
Adrian Pronk
1
@jjlin: grep -v '/$'solo omitirá las entradas de directorio en el archivo ZIP. Todavía incluirán entradas que eran archivos sin formato en el archivo ZIP pero que eran directorios preexistentes en la carpeta de destino. Por esta razón, sería prudente omitir-r
Adrian Pronk
11

Con el interruptor -Z1, descomprimir mostrará exactamente un archivo por línea (y nada más).

De esta manera, puedes usar

unzip -Z1 | xargs -I {} rm '{}'

para eliminar todos los archivos extraídos del archivo zip.

El comando

unzip -Z1 | xargs -I {} rm -rf '{}'

eliminará directorios también, pero debes tener cuidado. Si los directorios ya existían antes de extraer el archivo zip, también se eliminarán todos los archivos preexistentes en esos directorios.


Si va a volver a extraer el archivo zip de todos modos, hay otro enfoque que garantiza tratar nombres de archivos extraños.

Primero extraiga el archivo zip donde originalmente quiso extraerlo:

unzip file.zip -d elsewhere

Ahora, cambie al directorio donde extrajo los archivos por error y ejecute el siguiente comando:

find elsewhere -type f -printf "%P\0" | xargs -0 -I {} rm '{}'
  • -type f solo encuentra archivos (no directorios).

  • %P\0es la ruta relativa (sin elsewhere/), seguida de un carácter nulo.

  • -0hace que xargs separe líneas por caracteres nulos. Esto es más confiable, ya que, en teoría, los nombres de archivo pueden contener caracteres de nueva línea.


Para lidiar con los directorios sobrantes, puede ejecutar el comando:

find -type d -exec rmdir -p {} \; 2> /dev/null
  • -type d solo encuentra directorios.

  • -exec rmdir -p {} \;se ejecuta rmdir -p {}para cada directorio que se ha encontrado.

    {}es el directorio que se ha encontrado y el -pmodificador hace que rmdir elimine también sus directorios principales vacíos.

  • 2> /dev/null suprime los mensajes de error que surgirán al intentar eliminar directorios no vacíos o eliminados previamente.


Páginas man relacionadas:

Dennis
fuente
+1 por hacerme leer zipinfola página del manual.
terdon
Bueno, eso lo hace un poco más fácil. :)
jjlin
2

Aquí hay una solución aún más fácil y segura (creo)

zip -m getmeoutofhere.zip `unzip -lqq myoriginalzipfile.zip`
rm getmeoutofhere.zip

Qué está haciendo esto: el comando descomprimir entre comillas generará una lista de lo que estaba en su archivo original.

zip -m luego usará esa lista para agregar, agregar cada uno para getmeoutofhere.zip y eliminarlo del directorio original (por lo que, en teoría, debería ser una sangría a myoriginalfile.zip.

La desventaja es que descomprimir -lqq producirá texto adicional, fechas, horas, tamaño de archivo, etc. Esto hará que zip -m produzca mensajes de error, pero esto no debería afectar (a menos que tenga el caso improbable de un archivo con el mismo nombre).

Tenga en cuenta que esto no eliminará ningún directorio que se haya creado durante la descompresión original.

David E.
fuente
Enfoque interesante, explorará más a fondo.
mafp
1

Si extrajo los archivos de manera que la marca de tiempo de modificación en el archivo no se conserva en las copias extraídas (sino que los archivos extraídos tienen su hora de modificación habitual), entonces la forma correcta de atacar esto es a través del tiempo de modificación. Todos los archivos extraídos tienen una marca de tiempo de modificación más reciente que el archivo existente modificado más recientemente en ese directorio.

Aquí hay una situación simple.

Suponga que ninguno de los archivos existentes en el directorio actual se tocó durante al menos 24 horas. Cualquier cosa que se haya modificado en las últimas 24 horas es, por lo tanto, basura del archivo zip.

$ find . -mtime -1 -print0 | xargs -0 rm

Esto también encontrará algunos directorios, pero rmlos dejará solos. Se pueden tratar en un segundo pase:

$ find . -mtime 1 -type d -print 0 | xargs -0 rmdir

Cualquier directorio que haya sido modificado recientemente fue modificado por el zip. Si rmdirlos elimina con éxito, eso significa que están vacíos. Los directorios vacíos que fueron tocados por zip probablemente fueron creados por él: es decir, vinieron del archivo. No podemos estar 100% seguros. Es posible que el trabajo de descompresión coloque algunos archivos en un directorio existente que estaba vacío.

Si findla granularidad de 24 horas no es lo suficientemente buena para el trabajo, debido a que los archivos en el árbol se modificaron demasiado recientemente, entonces consideraría algo simple: supongamos que el trabajo de descompresión no incluye nada en los subdirectorios existentes. Es decir, todo lo que se descomprimió es un archivo en el nivel superior o un nuevo subdirectorio que no estaba allí antes, que por lo tanto no contiene más que material del zip. Entonces:

# list directory in descending order of modification time
$ ls -1t > filelist  # descending order of modification time

Ahora abrimos filelisten un editor de texto y determinamos la primera entrada en la lista que no vino del archivo zip. Eliminamos esa entrada y todo lo demás después de ella. Lo que queda son los archivos y directorios que vinieron del zip. Primero, inspeccionamos visualmente problemas como espacios en los nombres y ocurrencias de comillas que deben escaparse. Luego podemos agregar citas alrededor de todo, si es necesario: lo siguiente supone que usa Vim:

:%s/.*/"&"/

Luego únelo todo en una gran línea:

:%j

Ahora inserte rm -rfdelante de él:

Irm - rf<ESC>

Ejecute la línea debajo del cursor como un comando de shell:

!!sh<Enter>

Definitivamente, no automatizaría los pasos de esta tarea, debido al riesgo de borrar archivos que ya estaban allí, o arruinarlos debido a problemas de nombre de archivo.

Si va a seguir la ruta obvia de obtener una lista de las rutas en el archivo zip, luego captúrela en un archivo, revísela con mucho cuidado y transfórmela después de realizar cualquier edición necesaria.

Kaz
fuente