Rimozione di file con una determinata estensione tranne un file dal terminale


36

Ho bisogno di rimuovere tutti i file con estensione .gif tranne un file con nome dire "nomefile.gif". Qual è il modo ottimale per farlo nel terminale?

Il comando rm *.gifrimuove tutti i file gif incluso il file filename.gif.

Risposte:


66

Ecco la semplice soluzione che probabilmente dovresti usare:

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

Non c'è niente di speciale .keepnell'estensione, questo lo rende solo in modo che il nome file non finisca temporaneamente .gif.

Se non è necessario rinominare il file (e ci sono situazioni di scripting in cui questo è importante):

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

Oppure puoi scriverlo più breve in questo modo:

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

È preferibile utilizzare findinvece; è molto potente, potresti considerarlo più leggibile e gestisce meglio nomi di file strani con personaggi come *in essi .

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

Ho usato l' -notoperatore per la leggibilità, ma se la conformità POSIX è importante - se non stai usando GNU find, o se questo è per uno script che intendi ridistribuire ad altri o eseguire su una varietà di sistemi - dovresti usa !invece l' operatore:

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

Una cosa utile findè che puoi facilmente modificare il comando per la distinzione tra maiuscole e minuscole, in modo da trovare ed eliminare i file con estensioni come .GIF:

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

Si prega di notare che ho usato -inameal posto del -nameper il modello di ricerca *.gifma non ho , non utilizzato per filename.gif. Presumibilmente sai esattamente come si chiama il tuo file e -inamecorrisponderà a maiuscole / minuscole alternative non solo nell'estensione , ma ovunque nel nome del file.

Tutte queste soluzioni eliminano solo i file che risiedono immediatamente nella directory corrente . Non eliminano i file non contenuti nella directory corrente e non eliminano i file che risiedono nelle sottodirectory della directory corrente.

Se si desidera eliminare i file ovunque contenuti all'interno della directory corrente (ovvero, anche nelle sottodirectory e nelle sottodirectory di tali sottodirectory, e così via - file contenuti nella directory corrente o in uno dei suoi discendenti), utilizzare find senza maxdepth -1 :

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

Stai attento con questo!

Puoi anche impostare altri valori con -maxdepth. Ad esempio, per eliminare i file nella directory corrente e i suoi figli e nipoti, ma non più in profondità:

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

Assicurati solo di non mettere mai il -deleteprimo, prima delle altre espressioni! Vedrai che l'ho sempre messo -deletealla fine. Se lo metti all'inizio, verrebbe valutato per primo, e tutti i file in .(compresi i file che non finiscono .gife i file nelle directory sub-sub ... sub .) vengono eliminati!

Per ulteriori informazioni , consultare le pagine di manuale per bashe she per i comandi utilizzati in questi esempi: mv, rm, [, e (soprattutto) find.


1
Grazie Elia L'ho fatto in modo simile al primo metodo che hai suggerito, ovvero convertire il file in un formato diverso e riconvertirlo. Ma questo sembrava un po 'non ottimale e sporco. Mi piace il secondo approccio che tu e @efthialex avete suggerito e lo sto usando ora. Grazie!

1
@Marvis Sono contento che questo abbia soddisfatto le tue esigenze. A proposito, ho ampliato questa risposta con informazioni su un altro metodo (o classe di metodi) utilizzando find, che è altamente versatile.
Eliah Kagan,

3
Grazie per la risposta dettagliata findè davvero una buona scoperta.

1
+1 per la risposta di buon senso (sposta-cancella-sposta).
tu-Reinstate Monica-dor duh,

Nit-pick: come findgestisce i nomi di file strani con *in loro meglio del for-loop? Il ciclo for gestisce *bene i file , perché hai usato virgolette doppie.
Flimm,

28

Se si utilizza bash(la shell predefinita), l' extglobopzione shell consente di utilizzare una sintassi di corrispondenza del modello estesa. Per abilitarlo, utilizzare il shoptcomando incorporato:

shopt -s extglob

(Includo quella riga nel mio .bashrcfile.)

Tra le altre cose, consente l'accesso !()all'operatore, che corrisponde a qualsiasi modello non all'interno delle parentesi. Per il tuo scopo:

rm !(filename).gif

Maggiori informazioni sono disponibili in man bash"Pattern Matching".


1
Risposta eccellente! È specifico per Bash, quindi potrebbe non essere adatto per gli script portatili, ma attività interattive e persino alcuni script, questo è senza dubbio il modo migliore per andare. Presenta la complessità aggiuntiva che extglobdeve essere abilitata, ma non succede nulla di male se è disabilitato (in quel caso, semplicemente non funziona). E sembra essere abilitato per impostazione predefinita, anche se non ho shopt -s extglobin nessuno dei miei file di configurazione (o globale). Se c'è una semplice spiegazione del motivo per cui funziona per me fuori dalla scatola, potresti volerlo aggiungere a questa risposta; o potrei pubblicare una nuova domanda.
Eliah Kagan,

13

Apri un terminale con Ctrl+ Alt+ Te digita:

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

La differenza tra -deletee -exec rm -vf {} \;è che con la seconda opzione, sarai in grado di vedere quali file sono stati rimossi, a causa del -vflag. È qualcosa che non si può fare con l' -deleteopzione. ( -name -deletestamperà i nomi dei file, ma rmcon -vrivela se ogni file era stato eliminato con successo .)


1

C'è la GLOBIGNOREvariabile A partire dalman 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.

Quindi, un modo semplice è semplicemente impostare GLOBGINORE il nome del file in questione:

$ 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

Quindi, nel tuo caso:

GLOBIGNORE=filename.gif; rm *.gif

Naturalmente, poiché GLOBIGNOREcontiene motivi, è possibile utilizzarlo ogni volta che si desidera escludere un motivo:

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

Un vantaggio (?!) Di GLOBIGNOREè l'impostazione che esclude automaticamente .e.. dalla corrispondenza, e consente di 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.

Quindi, un modo semplice per eliminare tutti i file e le cartelle in una directory:

GLOBIGNORE=.; rm -r *

Il *corrisponderà nomi di file che iniziano con ., in quanto GLOBIGNOREabilitata dotglob, ma non corrisponderà .o .., in modo da non influenzare la directory padre in quel modo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.