Eliminar todos los archivos excepto algunos de un directorio

215

Al usar sudo rm -r, ¿cómo puedo eliminar todos los archivos, con la excepción de lo siguiente:

textfile.txt
backup.tar.gz
script.php
database.sql
info.txt
masjoko
fuente
55
Suena como una pregunta para unix.stackexchange.com
Jason
1
Hay 2 formas de leer esta pregunta, y las respuestas existentes cubren ambas interpretaciones: CUALQUIERA: (a) preservar los archivos con los nombres especificados ubicados directamente en el directorio de destino y, como rm -rimplica, eliminar todo lo demás, incluidos los subdirectorios , incluso si contienen archivos con los nombres especificados; O: (b) recorra todo el subárbol del directorio de destino y, en cada directorio, elimine todos los archivos excepto aquellos con los nombres listados.
mklement0

Respuestas:

219
find [path] -type f -not -name 'textfile.txt' -not -name 'backup.tar.gz' -delete

Si no especifica -type fbuscar, también se enumerarán los directorios, que quizás no desee.


O una solución más general usando la combinación muy útil find | xargs:

find [path] -type f -not -name 'EXPR' -print0 | xargs -0 rm --

por ejemplo, elimine todos los archivos que no sean txt en el directorio actual:

find . -type f -not -name '*txt' -print0 | xargs -0 rm --

La combinación print0y -0es necesaria si hay espacios en alguno de los nombres de archivo que deben eliminarse.

awi
fuente
2
parece xargs tiene un límite de tamaño de texto
frazras 05 de
26
para eliminar múltiples patrones:find . -type f -not -name '*ignore1' -not -name '*ignore2' | xargs rm
Siwei Shen 申思维
55
¿Qué pasa con los directorios? eliminará todos los archivos, pero ¿eliminará las carpetas?
orezvani
31
En lugar de "| xargs rm", find toma un parámetro -delete.
Emil Stenström
1
en lugar de -print0 | xargs -0 rm --usted puede simplemente-delete
Andy
151
rm !(textfile.txt|backup.tar.gz|script.php|database.sql|info.txt)

Extglob (Extended Pattern Matching) debe habilitarse en BASH (si no está habilitado):

shopt -s extglob
pl1nk
fuente
2
Recibo un "error de sintaxis cerca del token inesperado` ('"cuando lo hago shopt -s extglob; rm -rf !(README|LICENSE). ¿Alguna idea de por qué?
Dennis
@nic No lo recuerdo, lo siento. Probablemente terminé usando findla -deleteopción.
Dennis
Esta es la mejor solución para mí y funciona de manera predeterminada en mi Ubuntu 12.04.4 LTS sin necesidad de comprar
xtian
3
@nic, @Dennis: el error de sintaxis sugiere que bashse usó algo que no sea , por ejemplo dash, donde extglobno se admite. Sin embargo, en un shell interactivo bash , el comando TAMBIÉN no funcionará como se indica, aunque por diferentes razones. En resumen: ejecutar shopt -s extglobPOR SÍ MISMO; UNA VEZ CON ÉXITO HABILITADO (verificar con shopt extglob), ejecutar rm -rf !(README|LICENSE). (Si bien extglobaún no está activada, !(...)se evalúa por la expansión de historia antes de ejecutar los comandos, ya que esto probablemente no, NI se ejecuta el comando, y extglob. No está activada)
mklement0
2
@nic, @Dennis, @ mklement0: Tuve el mismo problema con "error de sintaxis cerca de token inesperado (" al ejecutar el comando dentro de un archivo .sh (#! / bin / bash) pero no cuando lo estaba ejecutando en un comando interfaz de línea. Resultó que además de la shopt -s extglobejecución en la interfaz de línea de comando, tuve que volver a ejecutarlo dentro de mi archivo de script. Esto resolvió el problema para mí.
Ahresse
41

find . | grep -v "excluded files criteria" | xargs rm

Esto enumerará todos los archivos en el directorio actual, luego enumerará todos aquellos que no coincidan con sus criterios (tenga cuidado con los nombres de directorio coincidentes) y luego elimínelos.

Actualización : en función de su edición, si realmente desea eliminar todo del directorio actual, excepto los archivos que enumeró, se puede usar:

mkdir /tmp_backup && mv textfile.txt backup.tar.gz script.php database.sql info.txt /tmp_backup/ && rm -r && mv /tmp_backup/* . && rmdir /tmp_backup

Creará un directorio de respaldo /tmp_backup(tiene privilegios de root, ¿verdad?), Moverá los archivos que enumeró a ese directorio, eliminará recursivamente todo en el directorio actual (sabe que está en el directorio correcto, ¿verdad?), Muévase volver al directorio actual de todo /tmp_backupy finalmente, eliminar /tmp_backup.

Elijo el directorio de respaldo para que esté en la raíz, porque si está tratando de eliminar todo de forma recursiva de la raíz, su sistema tendrá grandes problemas.

Seguramente hay formas más elegantes de hacer esto, pero esta es bastante sencilla.

darioo
fuente
2
También funciona muy bien con egrep, por ejemplo, para limpiar archivos intermedios de látex:find . | egrep -v "\.tex|\.bib" | xargs rm
mtsz
comando increíble! para eliminar directorios simplemente cambie a rm -r
Aris
1
Si desea simplemente eliminar todo en el directorio actual que no sea el criterio excluido: find . -maxdepth 1 | grep -v "exclude these" | xargs rm -rfunciona mucho más rápido ya que no necesita profundizar en directorios innecesariamente.
billynoah
Re findpipeline: dejando a un lado los problemas de eficiencia (se usan 3 comandos para lo que findpodría hacer solo), esto no funcionará como se espera con los nombres de archivo con espacios incrustados y potencialmente eliminará los archivos incorrectos.
mklement0
Un comando que almacena archivos "para ser guardados" en otra ubicación ( /tmp_backup) no termina bien si se interrumpe: desde la perspectiva del usuario, todos los archivos han desaparecido, a menos que sepan dónde buscarlos para recuperarlos. . Por eso no estoy a favor de este tipo de estrategia.
Craig McQueen
20

Suponiendo que los archivos con esos nombres existen en varios lugares en el árbol de directorios y desea conservarlos todos:

find . -type f ! -regex ".*/\(textfile.txt\|backup.tar.gz\|script.php\|database.sql\|info.txt\)" -delete
Pausado hasta nuevo aviso.
fuente
19

Puede usar la variable de entorno GLOBIGNORE en Bash.

Supongamos que desea eliminar todos los archivos excepto php y sql, luego puede hacer lo siguiente:

export GLOBIGNORE=*.php:*.sql
rm *
export GLOBIGNORE=

Establecer GLOBIGNORE de esta manera ignora php y sql de los comodines utilizados como "ls *" o "rm *". Entonces, el uso de "rm *" después de configurar la variable eliminará solo el archivo txt y tar.gz.

lo más duro
fuente
66
+1; Una alternativa más simple para configurar y restaurar la GLOBIGNOREvariable es usar una subshell:(GLOBIGNORE='*.php:*.sql'; rm *)
mklement0
19

Prefiero usar la lista de subconsultas:

rm -r `ls | grep -v "textfile.txt\|backup.tar.gz\|script.php\|database.sql\|info.txt"`

-v, --invert-match selecciona líneas no coincidentes

\| Separador

Nick Tsai
fuente
1
Solución elegante
Joshua Salazar
9

Como nadie lo mencionó:

  • copie los archivos que no desea eliminar en un lugar seguro
  • eliminar todos los archivos
  • mover los archivos copiados nuevamente a su lugar
gniourf_gniourf
fuente
2
Es más complicado porque tienes que ocuparte de los permisos después de copiarlos.
Romulus
2
@RemusAvram Puede usar los interruptores apropiados con cpo rsyncpara preservar los permisos. De todos modos, este es solo un método alternativo (dado como una sugerencia) que tiene su lugar aquí, como respuesta al OP.
gniourf_gniourf
Esto puede no ser apropiado en muchas situaciones en las que los archivos a retener están siendo utilizados activamente por otros procesos. También es engorroso que los archivos que se van a eliminar son solo un pequeño subconjunto y hay una gran cantidad de archivos involucrados.
codeforester
@codeforester: claro. Pero hay situaciones en las que es apropiado, sin embargo ... de hecho, realmente no veo el punto de tu comentario :).
gniourf_gniourf
6

Puede escribir un bucle for para esto ...%)

for x in *
do
        if [ "$x" != "exclude_criteria" ]
        then
                rm -f $x;
        fi
done;
mishunika
fuente
6

Un poco tarde para el OP, pero espero que sea útil para cualquiera que llegue mucho más tarde por Google ...

Encontré la respuesta de @awi y el comentario sobre -delete de @Jamie Bullock realmente útil. Una utilidad simple para que pueda hacer esto en diferentes directorios ignorando diferentes nombres / tipos de archivos cada vez con un mínimo de escritura:

rm_except (o como quieras nombrarlo)

#!/bin/bash

ignore=""

for fignore in "$@"; do
  ignore=${ignore}"-not -name ${fignore} "
done

find . -type f $ignore -delete

Por ejemplo, para eliminar todo excepto los archivos de texto y foo.bar:

rm_except *.txt foo.bar 

Similar a @mishunika, pero sin la cláusula if.

lhuber
fuente
5

Si está utilizando zshlo que recomiendo encarecidamente.

rm -rf ^file/folder pattern to avoid

Con extended_glob

setopt extended_glob
rm -- ^*.txt
rm -- ^*.(sql|txt)
sudo bangbang
fuente
4

Probarlo funcionó con:

rm -r !(Applications|"Virtualbox VMs"|Downloads|Documents|Desktop|Public)

pero los nombres con espacios son (como siempre) difíciles. Intenté también con Virtualbox\ VMslas comillas. Elimina siempre ese directorio ( Virtualbox VMs).

juanfal
fuente
NO funciona para archivos. rm !(myfile.txt)elimina todo incluidomyfile.txt
khaverim
debería ejecutarse shopt -s extglobprimero como señaló @ pl1nk
zhuguowei
Sí, es muy importante activar el globbing extendido ( extglob ) en bash, lo hago a diario, en algunos Macs en los laboratorios: shopt -s extgloby luego cd /Users/alumno/y finalmente rm -rf !(Applications|Virtualbox*VMs|Downloads|Documents|Desktop|Public|Library) Lea sobre globbing extendido aquí
juanfal
4

Sólo:

rm $(ls -I "*.txt" ) #Deletes file type except *.txt

O:

rm $(ls -I "*.txt" -I "*.pdf" ) #Deletes file types except *.txt & *.pdf
wyx
fuente
Esto no es recomendable. Analizar la lssalida puede convertirse en un problema es. Consulte aquí para obtener información detallada.
RJ
2

Haz los archivos inmutables. Ni siquiera root podrá eliminarlos.

chattr +i textfile.txt backup.tar.gz script.php database.sql info.txt
rm *

Todos los demás archivos han sido eliminados.
Finalmente, puede restablecerlos mutable.

chattr -i *
L'alligator des abers
fuente
0

Esto es similar al comentario de @ siwei-shen pero necesita la -obandera para hacer múltiples patrones. La -obandera significa 'o'

find . -type f -not -name '*ignore1' -o -not -name '*ignore2' | xargs rm

Daniel Chen
fuente
0

Puede hacer esto con dos secuencias de comandos. Primero defina una matriz con el nombre de los archivos que no desea excluir:

files=( backup.tar.gz script.php database.sql info.txt )

Después de eso, recorra todos los archivos en el directorio que desea excluir, verificando si el nombre del archivo está en la matriz que no desea excluir; si no es así, elimine el archivo.

for file in *; do
  if [[ ! " ${files[@]} " ~= "$file"  ]];then
    rm "$file"
  fi
done
Leonardo Hermoso
fuente
La comparación de expresiones regulares es incorrecta: conservará cualquier archivo cuyo nombre sea una subcadena de uno de los archivos protegidos (aunque los espacios circundantes mitigan algo; pero los nombres de archivo pueden contener espacios). Debe recopilar una matriz de todos los archivos, luego restar los archivos que desea excluir de esa matriz y luego eliminar los archivos restantes.
tripleee
-1

Como nadie ha mencionado esto aún, en un caso particular:

OLD_FILES=`echo *`
... create new files ...
rm -r $OLD_FILES

(o solo rm $OLD_FILES)

o

OLD_FILES=`ls *`
... create new files ...
rm -r $OLD_FILES

Es posible que necesite usar shopt -s nullglobsi algunos archivos pueden estar allí o no:

SET_OLD_NULLGLOB=`shopt -p nullglob`
shopt -s nullglob
FILES=`echo *.sh *.bash`
$SET_OLD_NULLGLOB

sin nullglob, echo *.sh *.bash puede darle "a.sh b.sh * .bash".

(Habiendo dicho todo eso, prefiero esta respuesta , aunque no funciona en OSX)

18446744073709551615
fuente
La respuesta existente de Leonardo Hermoso hace esto con menos errores. Esto fallará para los nombres de archivo con espacios; El uso de una matriz resuelve elegantemente ese problema en particular (y también evita de manera menos crucial el uso de mayúsculas para sus variables privadas, que generalmente se debe evitar).
tripleee
-3

En lugar de buscar un comando directo, mueva los archivos necesarios al directorio temporal fuera del directorio actual. Luego, elimine todos los archivos con rm *o rm -r *.

Luego mueva los archivos requeridos al directorio actual.

Ajeesh
fuente
Esto puede no ser apropiado en muchas situaciones en las que los archivos a retener están siendo utilizados activamente por otros procesos. También es engorroso que los archivos que se van a eliminar son solo un pequeño subconjunto y hay una gran cantidad de archivos involucrados.
codeforester
-3

Eliminar todo, excluir file.name:

ls -d /path/to/your/files/* |grep -v file.name|xargs rm -rf
mik-mak
fuente
2
Esto fallará horriblemente y podría causar la pérdida de datos si su directorio / archivos tienen espacios o caracteres inusuales. Nunca deberías analizar ls. mywiki.wooledge.org/ParsingLs
RJ