eliminar archivo pero excluir todos los archivos en una lista

17

Necesito limpiar una carpeta periódicamente. Me sale una lista de archivos que contiene texto, qué archivos están permitidos. Ahora tengo que eliminar todos los archivos que no están en este archivo.

Ejemplo:

dont-delete.txt:

dontdeletethisfile.txt
reallyimportantfile.txt
neverdeletethis.txt
important.txt

Mi carpeta de limpieza contiene esto como ejemplo:

ls /home/me/myfolder2tocleanup/:

dontdeletethisfile.txt
reallyimportantfile.txt
neverdeletethis.txt
important.txt
this-can-be-deleted.txt
also-waste.txt
never-used-it.txt

Entonces estos archivos deben ser eliminados:

this-can-be-deleted.txt
also-waste.txt
never-used-it.txt

Busco algo para crear un comando de eliminación con una opción para excluir algunos archivos proporcionados por archivo.

stefan83
fuente
¿Es esta una tarea?
mook765
Espero que no seas su maestro. lol
Gujarat Santana
2
@gujarat No somos un servicio de tarea gratuito, por lo que el comentario está justificado. En cuanto a la pregunta en sí, puede ser útil para otros, por lo que está abierta hasta ahora.
Sergiy Kolodyazhnyy
@Serg Estoy totalmente de acuerdo contigo
Gujarat Santana

Respuestas:

9

El rmcomando se comenta para que pueda verificar y verificar que funciona según sea necesario. Entonces simplemente descomente esa línea.

La check directorysección se asegurará de que no ejecute accidentalmente el script desde el directorio incorrecto y bloquee los archivos incorrectos.

Puede eliminar la echo deletinglínea para que se ejecute en silencio.

#!/bin/bash

cd /home/me/myfolder2tocleanup/

# Exit if the directory isn't found.
if (($?>0)); then
    echo "Can't find work dir... exiting"
    exit
fi

for i in *; do
    if ! grep -qxFe "$i" filelist.txt; then
        echo "Deleting: $i"
        # the next line is commented out.  Test it.  Then uncomment to removed the files
        # rm "$i"
    fi
done
LD James
fuente
Edité su código para evitar el uso inútills y la captura inútil de la salida de grepsi todo lo que quiere saber es si hubo una coincidencia o no. También utilicé patrones de cadenas fijas para evitar problemas de escape.
David Foerster
@DavidFoerster Gracias por la contribución. Sin embargo, cuando se cambió el whilebucle a un forbucle que ha cambiado inadvertidamente el iteration keyde ia f. en la declaración, que rompió el código. Lo arreglé.
LD James
Vaya, fuerza de la costumbre. Tiendo a abreviar nombres de variables de shell para nombres de archivo como f. ;-P (... y +1 por su respuesta que olvidé antes)
David Foerster
10

Este script de Python puede hacer esto:

#!/usr/bin/env python3
import os
no_remove = set()
with open('./dont-delete.txt') as f:
     for line in f:
         no_remove.add(line.strip())

for f in os.listdir('.'):
    if f not in no_remove:
        print('unlink:' + f ) 
        #os.unlink(f)

Parte importante es descomentar la os.unlink()función.

NOTA : agregue este script y dont-delete.txta su dont-delete.txtpara que ambos estén en la lista, y manténgalos en el mismo directorio.

Sergiy Kolodyazhnyy
fuente
1
Cambié su código para usar una setbúsqueda en lugar de una lista para O (1) en lugar de O (n) en la segunda parte.
David Foerster
gracias por su ayuda, normalmente soy un chico de Windows, pero las costuras de Python también son geniales =)
stefan83
1
@ stefan83: Python funciona igual de bien en Windows.
David Foerster
3

Aquí hay una frase:

comm -2 -3 <(ls) <(sort dont_delete) | tail +2 | xargs -p rm
  1. ls imprime todos los archivos en el directorio actual (en orden)
  2. sort dont_delete imprime todos los archivos que no queremos eliminar en orden
  3. el <()operador convierte una cadena en un objeto similar a un archivo
  4. Los commcomandos comparan dos archivos ordenados previamente e imprimen líneas en las que difieren
  5. el uso de las -2 -3marcas hace commque solo se impriman las líneas contenidas en el primer archivo pero no en el segundo, que será la lista de archivos que son seguros de eliminar
  6. la tail +2llamada es solo para eliminar el encabezado de la commsalida, que contiene el nombre del archivo de entrada
  7. Ahora obtenemos una lista de archivos para eliminar en la salida estándar. Canalizamos esta salida a la xargsque convertirá la secuencia de salida en una lista de argumentos para rm. La -popción obliga xargsa pedir confirmación antes de ejecutar.
cabeza de jardín
fuente
Gracias por su ayuda, ahora tengo mi solución!
stefan83
@gardenhead, cansé su código, pero elimina todos los archivos del directorio y mantiene solo el primer y el último archivo en la lista de no eliminar. ¿Tienes alguna idea para este problema? gracias por adelantado.
Negar
1

FWIW parece que puedes hacer esto de forma nativa zsh, usando el (+cmd)calificador global.

Para ilustrar, comencemos con algunos archivos

 % ls
bar  baz  bazfoo  keepfiles.txt  foo  kazoo

y un archivo de lista blanca

 % cat keepfiles.txt
foo
kazoo
bar

Primero, lea la lista blanca en una matriz:

 % keepfiles=( "${(f)$(< keepfiles.txt)}" )

o tal vez mejor

 % zmodload zsh/mapfile
 % keepfiles=( ${(f)mapfile[./keepfiles.txt]} )

(el equivalente de bash mapfile incorporado - o su sinónimo readarray). Ahora podemos comprobar si existe una clave (nombre de archivo) en la matriz ${keepfiles[(I)filename]}que devuelve 0 si no se encuentra ninguna coincidencia:

 % print ${keepfiles[(I)foo]}
1
 % print ${keepfiles[(I)baz]}
0
 %

Podemos usar esto para hacer una función que devuelva true si no hay coincidencias $REPLYen la matriz:

% nokeep() { (( ${keepfiles[(I)$REPLY]} == 0 )); }

Finalmente, usamos esta función como calificador en nuestro comando:

 % ls *(+nokeep)
baz  bazfoo  keepfiles.txt

o, en tu caso

 % rm -- *(+nokeep)

(Es probable que desee agregar el nombre del archivo de la lista blanca en sí).

conductor de acero
fuente
0

Suponiendo que su shell bash tiene el extglob shoptconjunto activado, aquí hay una alternativa algo más conservadora:

rm !($(tr \\n \| < keep.txt))

(... ¡acompañando la sugerencia de comunicación excelente de @ gardenhead!)

conny
fuente
0

A menos que la salida ls /home/me/myfolder2tocleanup/exceda el límite máximo de argumento de shell ARG_MAX que es de alrededor de 2 MB para Ubuntu, sugeriría lo siguiente.


Una implementación de comando de una línea que hará el trabajo sería la siguiente:

  1. Copie el dont-delete.txtarchivo en el directorio que contiene los archivos que se eliminarán de la siguiente manera:
cp dont-delete.txt /home/me/myfolder2tocleanup/
  1. cd al directorio que contiene los archivos que se eliminarán de la siguiente manera:
cd /home/me/myfolder2tocleanup/
  1. Realice una ejecución en seco para probar el comando y hacer que imprima los nombres de los archivos que detecta que se eliminarán sin eliminarlos realmente, de esta manera:
ls -p | grep -v / | sed 's/\<dont-delete.txt\>//g' | sort | comm -3 - <(sort dont-delete.txt) | xargs echo | tr " " "\n"
  1. Si está satisfecho con la salida, elimine los archivos ejecutando el comando de la siguiente manera:
ls -p | grep -v / | sed 's/\<dont-delete.txt\>//g' | sort | comm -3 - <(sort dont-delete.txt) | xargs rm

Explicación:

  • ls -penumerará todos los archivos y directorios en el directorio actual y la opción -pagregará /a los nombres de directorio.
  • grep -v /excluirá directorios al eliminar todos los elementos que contengan un /en sus nombres.
  • sed 's/\<dont-delete.txt\>//g'excluirá el dont-delete.txtarchivo, por lo que no se eliminará en el proceso.
  • sort, solo para asegurarse, clasificará la salida restante de ls.
  • comm -3 - <(sort dont-delete.txt)ordenará el dont-delete.txtarchivo, lo comparará con la salida ordenada lsy excluirá los nombres de archivo que existan en ambos.
  • xargs rmeliminará todos los nombres de archivo restantes en la salida ya procesada de ls. Esto significa que todos los elementos del directorio actual se eliminarán, excepto los directorios , los archivos enumerados en el dont-delete.txtarchivo y el dont-delete.txtarchivo mismo

En la parte de ejecución en seco:

  • xargs echo imprimirá los archivos que deben eliminarse.
  • tr " " "\n" traducirá espacios en nuevas líneas para facilitar la lectura.
Raffa
fuente
0

Sugiero utilizar la rsyncsolución publicada aquí ; de lo contrario, use la solución a continuación con una condición excepcional mencionada.

Suponiendo que no haya espacios en blanco (espacios / pestañas) en los archivos que figuran en un archivo llamado excludelist, entonces haría lo siguiente:

find /path/to -type f \( ! -name "excludelist" $(printf ' -a ! -name %s\n' $(< excludelist)) \)

Simplemente agregue -deleteal comando anterior para eliminar los archivos que no existen en el archivo excludelist . Si su hallazgo no tiene -deleteopción que puede utilizar rmcon -execla siguiente manera:

find /path/to -type f \( ! -name "excludelist" $(printf ' -a ! -name %s\n' $(< excludelist)) \) -exec echo rm {} \;

O usar -execcon +terminador en su lugar.

find /path/to -type f \( ! -name "excludelist" $(printf ' -a ! -name %s\n' $(< excludelist)) \) -exec echo rm {} +

echo solo se usa para correr en seco.

αғsнιη
fuente
-1

Mi sugerencia es:

sed -e 's/^/\.\//' dont-delete.txt > dont-delete-relative-path.txt
find . -type f -print | grep -Fxvf dont-delete-relative-path.txt | xargs -d'\n' rm

Actualizar 2018-08-07

Ejemplo:

1: mkdir /tmp/delete-example && cd /tmp/delete-example
2: touch a b c d
3: echo "./a\n./b\n./dont-delete.txt\n" > dont-delete.txt
4: find . -type f -print | grep -Fxvf dont-delete.txt | xargs -d'\n' rm

Tenga en cuenta que después de la línea 3 tendrá el dont-delete.txtarchivo con contenido:

./a
./b
./dont-delete.txt

(el liderazgo ./es muy importante )

Los archivos cy dserán eliminados.

nyxz
fuente
Intenté esto con un archivo de texto de los nombres de archivo separados por una nueva línea. Terminó eliminando todos los archivos en el directorio.
Jacques MALAPRADE
Supongo que su "lista de mantenimiento" estaba equivocada.
nyxz
He agregado ejemplos de uso.
nyxz