Carattere jolly `[!]` (punto esclamativo tra parentesi) in bash


10

Mi sono imbattuto in schemi e caratteri jolly strabilianti e di particolare interesse per me [!].

Questo costrutto è simile al [!]costrutto, tranne che per far corrispondere qualsiasi carattere tra parentesi, corrisponderà a qualsiasi carattere, purché non sia elencato tra [e ].

rm myfile [!192]

Quanto sopra credo che rimuoverà qualsiasi / tutti i file, ad eccezione di qualsiasi / tutti i file che hanno 192 nel loro nome.

Sono preoccupato tuttavia per il corretto utilizzo di questo con un'estensione di file e in particolare molteplici condizioni.

Quale sarebbe la sintassi corretta in una situazione del genere?

rm myfile [!.gif .csv. mp3] 

o

rm myfile [!.gif !.csv !.mp3]

La mia preoccupazione è che il periodo potrebbe essere fuori luogo, e quindi qualsiasi file con un .(che sarebbe sicuramente uno di loro?) Sarebbe quindi manipolato, quando sto cercando di causare la manipolazione di file specifici.

Questo costrutto è simile al [ ]costrutto, tranne che per far corrispondere qualsiasi carattere tra parentesi, corrisponderà a qualsiasi carattere, purché non sia elencato tra [e ].

(citato da http://www.tldp.org/LDP/GNU-Linux-Tools-Summary/html/x11655.htm )

Adesso; secondo me !è sufficiente un singolare , e tutti i valori che seguono sono contenuti all'interno dell'intervallo.


5
come consiglio generale, usa ls my-patterno echo my-commandper verificare se il globbing è fatto nel modo che preferisci, prima di corrererm
Wayne_Yux

Inoltre, ricorda che molti file, se non la maggior parte, non hanno estensioni. Le estensioni sui sistemi Linux sono generalmente irrilevanti e non vengono utilizzate per definire il tipo di file dalla maggior parte dei programmi.
terdon,

2
Nota che la tua prima citazione è citata in modo errato, ora sembra fondamentalmente dire che [!]è simile ma non simile[!]
ilkkachu

2
Vale la pena ricordare che ^ha lo stesso significato esatto di !in questo contesto, quindi [^a]esclude aesattamente come [!a]fa: Se il primo carattere che segue il [è un! oppure a ^ allora viene abbinato qualsiasi carattere non racchiuso. ( man bash)
dessert,

3
rm myfile [!192]rimuoverà myfile, così come qualsiasi singolo file-personaggio non di nome 1, 9o 2.
Kevin,

Risposte:


16

Citando man 7 glob:

An  expression  "[!...]"  matches  a single character, namely any character
that is not matched by the expression obtained by removing the first '!' from it.
(Thus, "[!]a-]" matches any single character except ']', 'a' and '-'.)

L'enfasi sulla parte del singolo carattere qui, []non può essere utilizzata per intere stringhe in Bash Pattern Matching.


bash's espansione delle parentesi graffe può essere utilizzata per abbinare stringhe, tuttavia non v'è alcun modo (che conosco) per negare loro. Per esempio

1.{gif,csv,mp3}

è espanso a

1.gif 1.csv 1.mp3

non importa se questi file esistono.


Come sottolineato da wchargin , è shoptpossibile abilitare gli operatori di corrispondenza dei modelli estesi, uno dei quali è !(). Dopo l'esecuzione shopt -s extglob, ad es

1.!(gif|csv|mp3)

è espanso a

1.jpg 1.bmp 1.png

se tali file esistono nella directory corrente. Per ulteriori informazioni man bash(sezione ESPANSIONE / Espansione percorso) e espansione Nome percorso (globbing) .


Per quello che stai cercando di fare, userei sempre find, che offre la negazione e molto altro, ad esempio nel modo seguente:

find . -maxdepth 1 -type f ! -name "*.gif" -a ! -name "*.csv" -a ! -name "*.mp3"

Questo elenca solo i risultati, se si desidera rimuoverli basta aggiungere l' -deleteopzione alla fine del comando. Come sempre con le azioni di rimozione: controllare sempre prima attentamente .

findoffre moltissime opzioni utili molto ben spiegate da man find.


1
Si noti che questo findcomando è ricorsivo ( modifica: come è stato mostrato in origine - Potrei conservare questo commento per le informazioni aggiuntive pertinenti ma non essenziali che trasmette). Per trovare i file che risiedono solo nella directory corrente ma non anche nelle sue sottodirectory - e quindi eliminare quei file solo se l' -deleteazione viene aggiunta - è possibile aggiungere -maxdepth 1immediatamente dopo find .. Ciò lo renderebbe in linea con l'effetto del globbing, poiché i glob non sono ricorsivi in ​​Bash a meno che l' globstaropzione shell non sia attivata e **venga utilizzata. Il glob mostrato nella domanda non è ricorsivo in tutte le shell.
Eliah Kagan,

2
"non c'è modo (che conosco) per negare loro" -con shopt -s extglob, è possibile utilizzare echo 1.!(gif|csv|mp3), che consente di stampare tutti 1.extdove extnon è gif, csvo mp3. (Vedi la sottosezione "Pattern Matching" di man bash.)
wchargin,

10

Penso che tu abbia frainteso l'uso delle parentesi. [abc]corrisponde a uno dei personaggi a, boppure c. [!abc]corrisponde a un personaggio che non lo è a, boppure c. Quindi il comandante

rm myfile [192]

rimuoverà myfileei file 1, 9e 2perché avete uno spazio tra miofile e la staffa. Al contrario, il comando

rm myfile[192]

rimuoverà i file myfile1, myfile9e myfile2.


vero. Meglio usare find.
pLumo,

In realtà [] è usato per un intervallo, [!] Un singolo personaggio
IronUhlan,

1
@IronUhlan, entrambi prendono un intervallo o un elenco di caratteri. È solo che l'altro è una negazione.
ilkkachu,

7

Le parentesi [ ]indicano una classe di caratteri. Qualsiasi singolo personaggio al loro interno può corrispondere nella posizione data. Così

file[192]

corrisponde a tre nomi possibili:

file1
file2
file9

Essa non corrisponde file192, perché si occupa solo con il singolo carattere dopo file.

Possiamo anche usare gli intervalli tra parentesi. [0-9]corrisponde a qualsiasi singola cifra nella posizione specificata. Abbiamo bisogno di due cifre [0-9][0-9]. Per una cifra seguita da una lettera maiuscola, potremmo usare [0-9][A-Z]...

! indica negazione.

file[!192]

abbinerà i file che hanno un singolo carattere dopo filetranne 1o 9o 2. Ad esempio corrisponderà file7e filese file%(e molti altri) ma non file192 , perché ha due caratteri extra.

Per il tuo caso d'uso, ti suggerisco di utilizzare findinvece di [!]. Ha un notoperatore.

È anche ricorsivo , il che significa che va in tutte le sottodirectory per impostazione predefinita. Puoi limitarlo alla directory corrente con -maxdepth 1, se è quello che vuoi. I caratteri jolly Shell (globbing) non sono ricorsivi (sebbene **sia possibile abilitare il globbing ricorsivo con ).

Supponendo che non si voglia evitare di eliminare i collegamenti simbolici, possiamo aggiungere -type dai test negati per evitare di eliminare eventuali directory. Grazie a Eliah Kagan per questo suggerimento.

find /path -maxdepth 1 -not \( -type d -or -iname "*.gif" -or -iname "*.csv" -or -iname "*.mp3" \)

Se vedi quello che vuoi, puoi eseguire di nuovo il comando con -delete

find /path -maxdepth 1  -not \( -type d -or -iname "*.gif" -or -iname "*.csv" -or -iname "*.mp3" \) -delete

2
E penso che puoi anche fare diverse gamme contemporaneamente. Ad esempio, la cifra esadecimale potrebbe essere[0-9a-fA-F]
Wilson,

@Zanna, si! In realtà stavo usando find :) Non mi rendevo conto che aveva un operatore "non" :)
IronUhlan,
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.