Eliminar subcarpetas vacías, mantener la carpeta principal

15

Cuando uso

find /home/user/parentdir -type d -empty -delete

busca recursivamente subcarpetas vacías dentro /home/user/parentdiry las elimina. Pero si /home/user/parentdirtambién está vacío, también elimina la parentdircarpeta, lo que no es deseable para mí.

Quiero guardar esto parentdiren rsyncalgunos archivos para hacer una copia de seguridad o en la nube. Después del procesamiento, necesito eliminar las carpetas vacías, pero parece improductivo volver a crear parentdircada vez.

¿Alguna sugerencia para mantener parentdir? Pensé en crear un .nocopyarchivo dentro de él y excluirlo rsync, pero parece excesivo. ¿Hay alguna forma más elegante?

TNT
fuente
si agrega una barra diagonal / al final de / parentdir (es decir, / parentdir /), ¿eso marca la diferencia?
Graham
66
-mindepth 1?
steeldriver
@Graham / parentdir / también elimina parentdir, por lo que no hay diferencia.
TNT
Ah, veo que me perdí el * al final que @Amourk menciona en su respuesta.
Graham

Respuestas:

25

Simplemente hazlo find /home/user/parentdir -mindepth 1 -type d -empty -delete.

Mira:

$ mkdir -p test1/test2
$ find test1 -type d
test1
test1/test2
$ find test1 -mindepth 1  -type d
test1/test2

La find /home/user/parentdir/* respuesta en AmourK no es deseable cuando hay muchos archivos y es demasiado complicada.

chx
fuente
15

Al agregar /*al final de parentdir, está realizando la acción en todos los subdirectorios de parentdir en lugar de en parentdirsí mismo. Y así, de la misma manera, /home/user/no se elimina en el comando anterior, parentdirno se eliminará en el siguiente comando. *se llama operador global y coincide con cualquier cadena de caracteres.

find /home/user/parentdir/* -type d -empty -delete

AmourK
fuente
77
Una cosa a tener en cuenta con este enfoque es que si hay una gran cantidad de archivos /home/user/parentdir/, el globo expandido puede exceder ARG_MAX, lo que resulta en un error de lista de argumentos demasiado largo . Puede reducir la posibilidad de que eso suceda cambiando el globo para */que solo coincida con los directorios.
steeldriver
12
También tenga en cuenta que esto no encontrará ningún niño que comience con un punto. Y algún día te darás cuenta de esto, y si también haces una búsqueda de ". *" Estarás en un gran mundo de dolor (porque ". *" Coincide ".."). Pregúntame cómo lo sé.
Glenn Willen
1
@GlennWillen, ¿por qué creo que deberíamos estar sentados en un bar con bebidas frente a nosotros antes de pedirte que cuentes esta historia?
Monty Harder
@MontyHarder Afortunadamente no fue tan malo como podría haber sido. Fue en mi primer sistema Unix, que era una máquina Solaris propiedad de mi escuela secundaria. Gracias a Dios que no estaba ejecutando 'rm', sino más bien 'chown'. Estaba usando root para tratar de arreglar la propiedad de todo en mi directorio de inicio. En cambio, me di la propiedad de CADA directorio de inicio (y todos sus contenidos) y luego tuve que volver a ponerlos a todos.
Glenn Willen
Tuve un compañero de trabajo que entrenaba a un nuevo empleado sobre cómo instalar una pieza de software. Implicaba ir /tmp, crear un subdirectorio, desempaquetar un tarball comprimido allí, ejecutar el instalador y limpiar. El novato pensó que rm -rf /tmp/foo-installera la forma correcta de hacerlo, y cuando la mano experimentada dijo "Noooooo" en su mejor impresión de Darth-Vader-al-final-del-Episodio III, el novato descubrió de la manera difícil qué tan cerca / y las teclas Enter estaban el uno al otro. Tuvimos que arrancar desde el disco de restauración del software de respaldo y volver a cargar todo el servidor desde la cinta.
Monty Harder
0

si tienes php-cli instalado,

printf %s $(pwd) | php -r 'function f(string $dir){var_dump($dir);$dir.=DIRECTORY_SEPARATOR;foreach(glob("$dir*",GLOB_ONLYDIR) as $d){f($d);}global $original;if(substr($dir,0,-strlen(DIRECTORY_SEPARATOR))!==$original && empty(glob("$dir*"))){rmdir($dir);}}f(($original=stream_get_contents(STDIN)));'
Hanshenrik
fuente
que querías foreach (RecursiveIteratorIterator(RecursiveDirectoryIterator($dir)) as $p) if (empty($skipped)) $skipped = TRUE; elseif ($p->isDir()) @rmdir($p->getPathname());o algo parecido a eso. $skippedse encarga de omitir el primero, @rmdirintenta eliminar un directorio y silenciosamente (esa es la @) falla si no está vacío.
chx
0

Cuando solo necesita un nivel (dejar una carpeta principal, pero eliminar carpetas secundarias vacías), un truco fácil es usar rmdir.

rmdir parent/*

Elimina todas las subcarpetas vacías, pero solo imprime un error para archivos y carpetas no vacías.

Esto no funciona de forma recursiva, pero al conocer la estructura de su carpeta puede ser la forma más rápida de hacer algo como

rmdir parent/*/*/* # deletes parent/a/b/c when empty
rmdir parent/*/* # deletes parent/a/b when it is now empty
rmdir parent/* # deletes parent/a when it is now empty

Ventaja: fácil de recordar, rápido de escribir, poco potencial para elegir un parámetro incorrecto.
Desventaja: Menos flexible, posiblemente muchos mensajes de error en su terminal.

allo
fuente