Eliminar archivos con una cierta extensión, excepto un archivo del terminal

36

Necesito eliminar todos los archivos con extensión .gif excepto un archivo con nombre que diga "filename.gif". ¿Cuál es la forma óptima de hacer esto en la terminal?

El comando rm *.gifelimina todos los archivos gif, incluido el archivo filename.gif.

Seth
fuente

Respuestas:

66

Aquí está la solución simple que probablemente debería usar:

mv filename.gif filename.gif.keep
rm *.gif
mv filename.gif.keep filename.gif

La .keepextensión no tiene nada de especial , esto solo hace que el nombre de archivo no termine temporalmente .gif.

Si no debe cambiar el nombre del archivo (y hay situaciones de secuencias de comandos donde esto es importante):

for X in *.gif; do
    if [ "$X" != "filename.gif" ]; then
        rm "$X"
    fi
done

O puedes escribirlo más corto así:

for X in *.gif; do [ "$X" != "filename.gif" ] && rm "$X"; done

Puede preferir usar finden su lugar; es muy poderoso, podría considerarlo más legible, y maneja mejor los nombres de archivos extraños con caracteres como *en ellos .

find . -maxdepth 1 -not -name 'filename.gif' -name '*.gif' -delete

He utilizado el -notoperador para facilitar la lectura, pero si el cumplimiento de POSIX es importante, si no está utilizando GNU find, o si se trata de un script que tiene la intención de redistribuir a otros o ejecutar en una variedad de sistemas, debería use el !operador en su lugar:

find . -maxdepth 1 ! -name 'filename.gif' -name '*.gif' -delete

Una cosa útil findes que puede modificar fácilmente el comando para que no se distinga entre mayúsculas y minúsculas, para que también encuentre y elimine archivos con extensiones como .GIF:

find . -maxdepth 1 -not -name 'filename.gif' -iname '*.gif' -delete

Tenga en cuenta que he utilizado -inameen lugar de -namepara el patrón de búsqueda *.gif, pero he no utilizado para filename.gif. Presumiblemente, usted sabe exactamente cómo se llama su archivo y -inamecoincidirá con mayúsculas alternativas no solo en la extensión , sino en cualquier parte del nombre del archivo.

Todas estas soluciones solo eliminan archivos que residen inmediatamente en el directorio actual . No eliminan archivos no contenidos en el directorio actual, y no eliminan archivos que residen en subdirectorios del directorio actual.

Si desea eliminar archivos en todas partes contenidos en el directorio actual (es decir, incluidos en subdirectorios, y en subdirectorios de esos subdirectorios, etc. - archivos contenidos en el directorio actual o cualquiera de sus descendientes), use find sin maxdepth -1 :

find . -not -name 'filename.gif' -name '*.gif' -delete

¡Ten cuidado con esto!

También puede establecer otros valores con -maxdepth. Por ejemplo, para eliminar archivos en el directorio actual y sus hijos y nietos pero no más profundo:

find . -maxdepth 3 -not -name 'filename.gif' -name '*.gif' -delete

¡Solo asegúrate de nunca poner -deleteprimero, antes de las otras expresiones! Verás que siempre pongo -deleteal final. Si lo pones al principio, se evaluaría primero, y todos los archivos debajo .(incluidos los archivos que no terminan en .gify los archivos en los directorios sub-sub-sub-sub profundos ... .) se eliminarán.

Para obtener más información , ver las páginas del manual para bashy shy para los comandos utilizados en estos ejemplos: mv, rm, [, y (especialmente) find.

Eliah Kagan
fuente
1
Gracias Elías Lo hice similar al primer método que sugirió, es decir, convertir el archivo en un formato diferente y volverlo a convertir. Pero eso parecía un poco subóptimo e inmundo. Me gusta el segundo enfoque que usted y @efthialex han sugerido y lo estoy usando ahora. ¡Gracias!
1
@ Marvis Me alegro de que esto haya satisfecho tus necesidades Por cierto, he ampliado esta respuesta con información sobre otro método (o clase de métodos) que utiliza find, que es muy versátil.
Eliah Kagan
3
Gracias por la respuesta detallada findes de hecho un buen hallazgo.
1
+1 para la respuesta de sentido común (mover-eliminar-mover).
tu-Reinstate Monica-dor duh
Nit-pick: ¿cómo findmaneja los nombres de archivos extraños con *ellos mejor que el for-loop? El bucle for maneja los archivos con *multa, porque ha usado comillas dobles.
Flimm
28

Si está utilizando bash(el shell predeterminado), la extglobopción de shell le permite usar una sintaxis extendida de coincidencia de patrones. Para habilitarlo, use el shoptcomando incorporado:

shopt -s extglob

(Incluyo esa línea en mi .bashrcarchivo).

Entre otras cosas, otorga acceso al !()operador, que coincide con cualquier patrón que no se encuentre dentro de los elementos parentales. Para su propósito:

rm !(filename).gif

Hay más información disponible en man bash"Coincidencia de patrones".

eswald
fuente
1
Excelente respuesta! Es específico de bash, por lo que puede no ser adecuado para secuencias de comandos portátiles, pero para tareas interactivas e incluso algunas secuencias de comandos, esta es sin duda la mejor manera de hacerlo. Lleva la complejidad adicional que extglobdebe habilitarse, pero no pasa nada malo si está deshabilitada (en ese caso, esto simplemente no funciona). Y parece estar habilitado de manera predeterminada, aunque no tengo shopt -s extglobninguno de mis archivos de configuración (o los globales). Si hay una explicación simple de por qué me funciona de manera inmediata, puede agregarla a esta respuesta; o puedo publicar una nueva pregunta.
Eliah Kagan
13

Abra una Terminal con Ctrl+ Alt+ Ty escriba:

find . -type f -name "*.gif" -and -not -name "filename.gif" -exec rm -vf {} \;

La diferencia entre -deletey -exec rm -vf {} \;es que con la segunda opción, podrá ver qué archivos se han eliminado, debido a la -vbandera. Eso es algo que no se puede hacer con la -deleteopción. ( -name -deleteImprimirá los nombres de archivo, pero rmcon -vrevela si cada archivo fue exitosamente eliminado.)

efthialex
fuente
1

Ahí está la GLOBIGNOREvariable. De man bash:

GLOBIGNORE
      A colon-separated list of patterns defining the set of filenames
      to be ignored by pathname expansion.  If a filename matched by a
      pathname expansion pattern also matches one of the  patterns  in
      GLOBIGNORE, it is removed from the list of matches.

Entonces, una forma simple es establecer GLOBGINOREel nombre de archivo en cuestión:

$ touch {a,b,c,d}.png
$ echo *.png
a.png b.png c.png d.png
$ GLOBIGNORE=c.png
$ echo *.png
a.png b.png d.png

Entonces, en tu caso:

GLOBIGNORE=filename.gif; rm *.gif

Por supuesto, dado que GLOBIGNOREcontiene patrones, puede usarlo siempre que desee excluir un patrón:

$ GLOBIGNORE='[a-c].png'; echo *.png
d.png
$ touch bd.png; GLOBIGNORE='?.png'; echo *.png
bd.png

Una ventaja (?!) GLOBIGNOREEs que su configuración excluye automáticamente .y.. de ser emparejado, y permite dotglob:

The  GLOBIGNORE shell variable may be used to restrict the set of file‐
names matching a pattern.  If GLOBIGNORE is set, each matching filename
that also matches one of the patterns in GLOBIGNORE is removed from the
list of matches.  The filenames ``.''  and ``..''  are  always  ignored
when  GLOBIGNORE is set and not null.  However, setting GLOBIGNORE to a
non-null value has the effect of enabling the dotglob shell option,  so
all other filenames beginning with a ``.''  will match.  To get the old
behavior of ignoring filenames beginning with a ``.'', make ``.*''  one
of  the  patterns  in  GLOBIGNORE.  The dotglob option is disabled when
GLOBIGNORE is unset.

Entonces, una forma simple de eliminar todos los archivos y carpetas en un directorio:

GLOBIGNORE=.; rm -r *

El *coincidirá con nombres de archivo que comienzan con ., ya GLOBIGNOREhabilitada dotglob, pero no va a coincidir .o .., por lo que no afectará el directorio padre de esa manera.

muru
fuente