Unix: elimina file e cartelle escluso PATTERN


10

Di recente mi sono trovato di fronte al compito di eliminare tutti i file / cartelle in una directory escludendo quelli corrispondenti a un modello specifico. Quindi ho elaborato un comando unix a riga singola per fare il lavoro. Deve essere solo una riga? Suppongo di no, ma è decisamente più bello in quel modo!

Mentre il problema è piuttosto semplice, sono rimasto un po 'sorpreso da quanto sia complessa la mia soluzione. Ecco il comando che ho usato; NOTA: questa è una soluzione scadente perché non gestisce nomi di file contenenti caratteri di avanzamento riga (che non importa nella mia situazione).

ls | grep -v PATTERN | xargs -n1 -IREPLACE rm -rf REPLACE

Non ho usato il comando "trova" perché non voglio ricorrere in cartelle corrispondenti a PATTERN. Ad esempio, considera la seguente struttura di file:

file_foo.txt
first_dir
  |
  +--> contents
  +--> ...
foo_dir
  |
  +--> anotherfile.txt
  +--> morefiles.log
foo_file.txt
somefile.txt

L'uso del modello "pippo" deve solo rimuovere "first_dir" (e ovviamente i suoi contenuti) e "somefile.txt" ( non "anotherfile.txt" o "morefiles.log").

Domanda, ci sono modi migliori (più eleganti e corretti) per raggiungere questo obiettivo?


EDIT:
Di recente è stato portato alla mia attenzione che "trova" potrebbe essere un'opzione migliore :

find * -maxdepth 0 ! -name PATTERN -print0 | xargs -0n1 rm -rf

Questa soluzione gestisce correttamente i percorsi contenenti caratteri di avanzamento riga.


EDIT: modificato "the_dir" errato per correggere "first_dir".
joxl

Risposte:


10

I seguenti esempi hanno il echoprefisso in modo da poter testare i modelli prima di usarli effettivamente. Rimuovere il echoper attivare il rm -rf. Sostituire rm -riper richiedere conferma.

ksh ha un'estensione di corrispondenza negativa nei suoi globbing:

# ksh
echo rm -rf !(*foo*)

La stessa sintassi è disponibile in bash se si imposta l' extglobopzione:

# bash
shopt -s extglob
echo rm -rf !(*foo*)

zsh ha una sua sintassi per questo:

# zsh
setopt extended_glob
echo rm -rf ^*foo*

Può anche usare la sintassi in stile ksh :

# zsh: ksh-style glob syntax
setopt ksh_glob no_bare_glob_qual
echo rm -rf !(*foo*)

# zsh: ksh-style glob syntax, adapted for the default bare_glob_qual option
setopt ksh_glob bare_glob_qual
echo rm -rf (!(*foo*))

Semplice, elegante, gestisce i caratteri di avanzamento riga, (quasi) una riga (ho aggiunto shopt -s extglobal mio file .bashrc, quindi ora è a riga singola). Perfetto! Prometto un voto non appena la mia reputazione lo consente.
joxl

7

Ecco un'altra findsoluzione. Non sono sicuro che questo abbia un reale vantaggio rispetto al tuo, ma non è necessario xargse consente la rara possibilità che * si espanda a troppi nomi.

find . -maxdepth 1 ! -name PATTERN -type f -delete

Ho anche aggiunto in -type fmodo che non tentasse di eliminare le directory.

Attenzione: -deleteè potente. Ho dato a uno dei miei file di test 0 autorizzazioni e il comando sopra lo ha eliminato senza esitazione.


6
Prima prova. Sostituisci -deletecon -printper vedere se trova i file desiderati. Se tutto va bene, utilizzare la cronologia dei comandi (ad esempio, premere il pulsante su) per tornare al comando precedente per assicurarsi di lavorare con gli stessi filtri di ricerca.
Doug Harris,

Leggere attentamente, nota che io faccio desiderio di directory rimuovere. E find -deletenon rimuoverà le directory non vuote. Si noti inoltre che find -maxdepth 1corrisponde a "." (directory corrente) che è davvero male. Anche se mi piace la tua idea di eliminazione xargs, si potrebbe usare in find -execalternativa. find * -maxdepth 0 ! -name PATTERN -exec rm -rf '{}' \;
joxl,

0

È possibile utilizzare questo esempio di script rebol http://askcodegeneration.com/keep-subversion/ invece che è molto più comprensibile e che può gestire l'interazione dell'utente come la selezione e la conferma della cartella:

delete-file: func[file][
    if/else none? find file "/.svn/" [
        delete file
    ][
        print ["keeping" file]
    ]
]
dir: request-dir
ans: ask rejoin ["Do you confirm " dir "? (Yes): "]
if (ans = "Yes") [
    foreach-file dir :delete-file
]
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.