Escludere da * nella riga di comando


14

Ci sono molte situazioni in cui l'uso di a *è praticamente inevitabile, ad esempio rm -rf *in una cartella che contiene migliaia di sottocartelle e file.

Ma cosa succede se si desidera escludere solo uno o due file o cartelle da rm comando? Ho cercato su Google e ho trovato solo soluzioni abbastanza complicate find . -depth -not \( -name 'one' -o -name 'two' \ -o -name 'three' \) -exec rm {} \;come indicato qui .

Esiste la possibilità di farlo in un modo più semplice, senza quella deviazione find ? Ad esempio rm -rf --exclude='one' --exclude='two' --exclude='three' *come in rsync o semplicemente rm -rf -e 'one','two','three' *?

Forse anche un possibilità generale di escludere le cose da *(in modo che altri comandi come cp, mv, ... non c'è bisogno di implementare il proprio)? Qualcosa del genere *{'one','two','three'}?


3
Se si desidera conservare solo uno o due file, il modo più semplice e semplice sarebbe quello di spostare quei file in un'altra posizione, cancellare tutti i file rimanenti e spostare quelli che si sono tenuti indietro. Se vuoi conservare molti più file, userei findcon l' --deleteopzione (non è necessario eseguire rmper ogni file. Questo è inutile sovraccarico).
Hennes,

Sarebbe una possibilità, ma è quasi elaborato come quello che ho citato. So che potrei combinare i comandi a qualcosa di simile mv -t /tmp one two three && rm -rf * && mv -t . /tmp/one /tmp/two /tmp/three, ma preferirei una soluzione che dà la possibilità di escludere esplicitamente qualcosa da *. Ci saranno sicuramente situazioni in cui lo spostamento o la copia dei file in un'altra destinazione non sarà un'opzione.
David,

2
Lo è, motivo per cui non l'ho pubblicato come risposta. Solo come un modo meno complesso di fare le cose (tre comandi molto semplici contro uno un po 'più complesso). Odio la complessità in combinazione con rm.
Hennes,

Risposte:


33

Per Bash esiste un'opzione shell chiamata extglobche è disattivata di default per mantenere la compatibilità con la sintassi della shell standard. Extglob completa la sintassi di operatori aggiuntivi come !(),?() , @()e un po 'di più.

Per accendere extglob, digitareshopt -s extglob . Per mantenerlo acceso per il tipo di utente corrente echo 'shopt -s extglob' >> ~/.bashrc.

Per l'esempio rm: con extglobte puoi usare

rm -rf !(one|two|three)

Sembra una buona soluzione, ma non voglio nemmeno accenderlo e spegnerlo ogni volta che mi trovo in una situazione come quella descritta.
David,

3
@ David: potresti mettere la shoptcosa nel tuo ~/.bashrc, non può far male.
enzotib,

Se ho capito bene, l'opzione extglob non cambia affatto l'effetto *, ma aggiunge invece indicatori simili (come i !) che hanno funzioni aggiuntive? In quel caso sarebbe una soluzione adorabile.
David,

1
@ David: Esatto.
Choroba,

1
@David Per mantenere la compatibilità con la sintassi della shell standard . Ci sono probabilmente altre situazioni in cui il comportamento può cambiare.
Gerrit,

4

Ho costruito tranne esattamente per questo (sry che non ero ancora in grado di aggiungere documentazione).

Ho una cartella come la seguente:

$ ls
a b c d

Digitare except b dti darà aec

$ except b d
ac

Ora puoi reindirizzare a rm.

except b d | xargs -0 rm

Questo può essere difficile da ricordare, quindi perché non creare semplicemente uno script oltre a, chiamato rm-except:

#!/usr/bin/env bash

except "$@" | xargs -0 rm

Allo stesso modo è facile ls-except:

#!/usr/bin/env bash

except "$@" | xargs -0 ls

3
Questo è carino, ma non è davvero necessario quando si conosce extglob - askubuntu.com/a/329233/138369
David

1

In alternativa a extglob (anche se questa è un'ottima risposta e tutti dovrebbero avere shopt -s extglob globstarnel loro .bashrc), è possibile utilizzare la variabile globale $ GLOBIGNORE . Diciamo che vuoi ottenere tutti i file tranne 'foo.txt' e 'bar baz.txt':

GLOBIGNORE=foo.txt:'bar baz.txt'

... tuttavia, questo attiverà l'opzione shell dotglob, il che significa che *corrisponderà ai file che iniziano con un punto (che sono normalmente nascosti). Quindi in realtà hai bisogno di due comandi:

GLOBIGNORE=foo.txt:'bar baz.txt'
shopt -u dotglob

Poiché si tratta di una variabile globale, influirà su tutti i glob che usi fino a quando $ GLOBSTAR non viene disattivato disconnettendosi o con

GLOBIGNORE=

Funzionerà anche solo sulle stringhe letterali passate alla variabile. Puoi vedere cosa intendo impostando $ GLOBIGNORE e osservando la differenza tra questi comandi:

printf '%s\n' *
printf '%s\n' ./*
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.