Eliminar todas las carpetas dentro de una carpeta excepto una con un nombre específico


17

Necesito eliminar todas las carpetas dentro de una carpeta usando un script diario. La carpeta para ese día debe dejarse.

La carpeta 'myfolder' tiene 3 subcarpetas: 'test1', 'test2' y 'test3'. Necesito eliminar todo excepto 'test2'.

Estoy tratando de hacer coincidir el nombre exacto aquí:

find /home/myfolder -type d ! -name 'test2' | xargs rm -rf

O

find /home/myfolder -type d ! -name 'test2' -delete

¡Este comando siempre intenta eliminar la carpeta principal 'myfolder' también! Hay alguna manera de evitar esto ?


66
En Unix y Linux, llamamos a estos "directorios", no "carpetas".
tchrist

1
Dependiendo de su shell, es posible que necesite citar ese !operador: \!o '!'.
Toby Speight

Respuestas:


32

Esto eliminará todas las carpetas dentro, ./myfolderexcepto que ./myfolder/test2se conservará todo su contenido:

find ./myfolder -mindepth 1 ! -regex '^./myfolder/test2\(/.*\)?' -delete

Cómo funciona

  • find inicia un comando de búsqueda.
  • ./myfolderle dice a find que comience con el directorio ./myfoldery sus contenidos.

  • -mindepth 1 no coincidir ./myfolder, solo los archivos y directorios debajo de él.

  • ! -regex '^./myfolder/test2\(/.*\)?' le dice a find que excluya ( !) cualquier archivo o directorio que coincida con la expresión regular ^./myfolder/test2\(/.*\)?. ^coincide con el inicio del nombre de la ruta. La expresión (/.*\)?coincide con (a) una barra inclinada seguida de algo o (b) nada en absoluto.

  • -delete le dice a find que elimine los archivos coincidentes (es decir, no excluidos).

Ejemplo

Considere una estructura de directorio que se parece a;

$ find ./myfolder
./myfolder
./myfolder/test1
./myfolder/test1/dir1
./myfolder/test1/dir1/test2
./myfolder/test1/dir1/test2/file4
./myfolder/test1/file1
./myfolder/test3
./myfolder/test3/file3
./myfolder/test2
./myfolder/test2/file2
./myfolder/test2/dir2

Podemos ejecutar el comando find (sin -delete) para ver qué coincide:

$ find ./myfolder -mindepth 1 ! -regex '^./myfolder/test2\(/.*\)?'
./myfolder/test1
./myfolder/test1/dir1
./myfolder/test1/dir1/test2
./myfolder/test1/dir1/test2/file4
./myfolder/test1/file1
./myfolder/test3
./myfolder/test3/file3

Podemos verificar que esto funcionó mirando los archivos que quedan:

$ find ./myfolder
./myfolder
./myfolder/test2
./myfolder/test2/file2
./myfolder/test2/dir2

1
Alternativa para -prunedejar los test2/*/subdirectorios solos: volver rm -ry agregar -maxdepth 1.
Toby Speight

@ Isaac OK. Hecho. (Además, +1 por su excelente respuesta.)
John1024

Excelente trabajo !, pero lo siento: eso eliminará todos los archivos dentro ./myfolder. Necesita una falta (IMvhO) solo-type d para directorios .
Isaac

Ok, esto debería funcionar como quieras:find ./myfolder -depth -mindepth 1 -maxdepth 1 -type d ! -regex '^./myfolder/test2\(/.*\)?'
Isaac

10

Usando bash :

shopt -s extglob
rm -r myfolder/!(test2)/

Ejemplo:

$ tree myfolder/
myfolder/
├── test1
│   └── file1
├── test2
│   └── file2
└── test3
    └── file3

$ echo rm -r myfolder/!(test2)
rm -r myfolder/test1 myfolder/test3
$ rm -r myfolder/!(test2)
$ tree myfolder/
myfolder/
└── test2
    └── file2

1 directory, 1 file

5

tl; dr

find ./myfolder -mindepth 1 -maxdepth 1 -type d -not -name test2 \
     -exec echo rm -rf '{}' \;

Elimine echo si está satisfecho con la lista de archivos.


El uso -mindepth 1garantizará que el directorio superior no esté seleccionado.

$ find ./myfolder -mindepth 1 -type d
./myfolder/test2
./myfolder/test2/one
./myfolder/test2/two
./myfolder/test
./myfolder/test/a1
./myfolder/test/a1/a2
./myfolder/test/a1/a2/a3

Sin embargo, una -not -name test2será no evitar subdirectorios en el interior test2:

$ find ./myfolder -mindepth 1 -type d -not -name 'test2'
./myfolder/test2/one
./myfolder/test2/two
./myfolder/test
./myfolder/test/a1
./myfolder/test/a1/a2
./myfolder/test/a1/a2/a3

Para hacer eso, necesitas algo como ciruela pasa:

$ find ./myfolder -mindepth 1 -name test2 -prune -o -type d -print
./myfolder/test
./myfolder/test/a1
./myfolder/test/a1/a2
./myfolder/test/a1/a2/a3

Pero no lo use delete, como implica depthy eso comenzará a borrarse del camino más largo:

$ find ./myfolder -depth -mindepth 1 -name test2 -prune -o -type d -print
./myfolder/test/a1/a2/a3
./myfolder/test/a1/a2
./myfolder/test/a1
./myfolder/test

Utilice rm -rf(elimine echosi realmente desea borrar):

$ find ./myfolder -mindepth 1 -name test2 -prune -o -type d -exec echo rm -rf '{}' \;
rm -rf ./myfolder/test
rm -rf ./myfolder/test/a1
rm -rf ./myfolder/test/a1/a2
rm -rf ./myfolder/test/a1/a2/a3

O use tambiénmaxdepth si todo lo que necesita es eliminar directorios (y todo lo que está dentro) (elimine el echopara borrar realmente):

$ find ./myfolder -mindepth 1 -maxdepth 1 -type d -not -name test2 -exec echo rm -rf '{}' \;
rm -rf ./myfolder/test

A -deletetodavía fallará si el directorio no está vacío:

$ find ./myfolder -mindepth 1 -maxdepth 1 -type d -not -name test2 -delete
find: cannot delete ‘./myfolder/test’: Directory not empty

2

Si está usando zsh, entonces podría:

setopt extended_glob # if you don't have it enabled

rm -rf myfolder/^test2

0

Probado con el siguiente comando y funcionó bien

find  /home/myfolder -maxdepth 1 -type d ! -iname test2 -exec rm -rvf {} \;

Te has encontrado con el mismo problema que el OP; La lista / home / carpeta en la línea de comandos (sin la crítica -mindepth 1) hace que el directorio superior coincida con todos los criterios (es un directorio y no se llama "test2") y, por lo tanto, se elimina.
Jeff Schaller
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.