Problemi di espressione regolare in Bash: [^ negate] non sembra funzionare


8

Quando eseguo ls /directory | grep '[^term]'in Bash ottengo un elenco regolare, come se il grepcomando fosse ignorato in qualche modo. Ho provato la stessa cosa con egrep, ho provato ad usarlo con virgolette doppie e singole, ma senza risultati migliori. Quando provo ls /directory | grep '^[term]ottengo tutte le voci che iniziano con il termine, come previsto.

Ho provato questo comando in un editor online, dove posso provare il mio regex e ha funzionato come dovrebbe. Ma non a Bash. Quindi funziona in una simulazione, ma non nella vita reale.

Lavoro su Crunchbang Linux 10. Spero che questa sia un'informazione sufficiente e non vedo l'ora di ogni suggerimento, perché non riuscire a eseguire un livello così basso e perdere ore è davvero frustrante!


Sono confuso a causa della negazione nel titolo. Vuoi greplinee che iniziano con il termine. Oppure vuoi grep per le linee che non contengono affatto un termine?
Bernhard

@ Bernardo: voglio un elenco senza il termine tra parentesi quadre. Non deve essere esattamente "termine"! Per quanto ho capito, [^ abc] significa che tutto ciò che contiene a, bec o qualsiasi combinazione di esso non dovrebbe essere nell'elenco.
1313

Risposte:


12

Sei sicuro di quello che vuoi che accada? Quando corri, ls /directory | grep '[^term]'stai essenzialmente prendendo in giro non le lettere. Ciò significa che se un file ha altre lettere nel suo nome, apparirà comunque nell'output di ls. Prendi ad esempio la seguente directory:

$ ls
alpha  brave  bravo  charlie  delta

Ora se corro ls |grep '^[brav]'ottengo il seguente:

$ ls |grep '^[brav]'
alpha
brave
bravo

Come puoi vedere, non solo ho avuto bravee bravoho anche alphaperché la classe di personaggi []riceverà qualsiasi lettera da quella lista.

Di conseguenza, se corro, ls |grep '[^brav]'otterrò tutti i file che non contengono i caratteri brav in qualsiasi parte del nome.

$ ls |grep '[^brav]'
alpha
bravo
brave
charlie
delta

Se noti che includeva l'intero elenco di directory perché tutti i file avevano almeno una lettera che non era inclusa nella classe di caratteri.

Quindi, come ha detto Kanvuanza, per invocare l'inverso del "termine" in contrapposizione ai personaggi t e r mche dovresti fare usando grep -v.

Per esempio:

$ ls |grep -v 'brav'
alpha
charlie
delta

Inoltre, se non desideri utilizzare i file che contengono caratteri nella classe grep -v '[term]'. Ciò impedirà la visualizzazione di qualsiasi file con uno di questi caratteri. (La risposta di Kanvuanza)

Per esempio:

$ ls |grep -v '[brav]'

Come puoi vedere non c'erano file elencati perché tutti i file in questa directory includevano almeno una lettera di quella classe.

Addendum:

Volevo aggiungere che usando PCRE è possibile usare solo regex per filtrare usando espressioni negative. Per fare questo si può usare qualcosa di noto come un fatto negativo look-ahead regex: (?!<regex>).

Quindi, usando l'esempio sopra, puoi fare qualcosa del genere per ottenere i risultati desiderati senza usare le grepbandiere.

$ ls | grep -P '^(?!brav)'
alpha
charlie
delta

Per decostruire quella regex, prima corrisponde all'inizio di una riga ^e quindi cerca le stringhe che non corrispondono bravda seguire in seguito. Solo alpha, charliee deltaabbina in modo che quelli siano gli unici stampati.


1
Ciò significa che se un file ha altre lettere nel suo nome, apparirà comunque nell'output di ls. Questo risponde ad alcune domande! :) Quindi il modo migliore per il momento sembra essere l' -vopzione. Grazie per il vostro sostegno! Questa domanda ha davvero rovinato il mio pomeriggio, dove la tua risposta illumina la mia serata!
1313

+1 per negative look-ahead regex.
Abhishek Kashyap,

3

Immagino che quella grep -vbandiera faccia quello che vuoi. Dalla pagina man :

-v, --invert-match
    Invert the sense of matching, to select non-matching lines.

È possibile utilizzare ls /directory | grep -v [term]per stampare qualsiasi riga non corrispondente.


Sono a conoscenza di questa opzione, ma mi sbaglio nel ritenere che [^ xyz] è l'opposto di [xyz] e dovrebbe funzionare in ogni caso? Voglio anche evitare di modificare qualsiasi impostazione ovunque a un livello così basilare. L'uso di un'opzione di inversione e / o di modifica delle impostazioni è sicuramente un buon modo per aggirare, ma per quanto ho capito, questo dovrebbe funzionare senza, fuori dalla scatola.
1313

Immagino che tu abbia ragione, questa è la notazione comune per negazione di classe (es. [^abc]Ma. Sono abbastanza sicuro che grep non supporta le negazioni di classe, tranne alcune standard (ad es. [[:^digits:]]). Il supporto di Grep per negazione è orribile !
Pedro Lacerda,

Il supporto di Grep alla negazione è orribile! E questi sono i suggerimenti che sono la vera ciliegina sulla torta. Ho gli stessi problemi con egrep e al momento non sono in grado di usare [almeno per me apparentemente] comandi più avanzati. Puoi suggerire un comando che fornisce risultati migliori e meno mal di testa?
1313

@ cellar.dweller, grepla gestione delle classi di personaggi va bene. Significa solo qualcosa di molto diverso da quello che tu (hai) capito. [abc]significa uno a, bo c; [^abc]significa tutt'altro che quanto sopra. È un personaggio.
vonbrand

@ cellar.dweller: Penso che il tuo problema più grande sia un fraintendimento di regex, in particolare le classi di personaggi all'interno di regex.
tink
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.