Poiché nessun altro ha dato una risposta diretta alla domanda che è stata posta , lo farò.
La risposta è che con POSIX grep
è impossibile soddisfare letteralmente questa richiesta:
grep "<Regex for 'doesn't contain hede'>" input
Il motivo è che POSIX grep
deve funzionare solo con le espressioni regolari di base , che semplicemente non sono abbastanza potenti per eseguire tale compito (non sono in grado di analizzare le lingue regolari, a causa della mancanza di alternanza e parentesi).
Tuttavia, GNU grep
implementa estensioni che lo consentono. In particolare, \|
è l'operatore di alternanza nell'implementazione di BRE delle GNU \(
e \)
sono le parentesi. Se il tuo motore di espressioni regolari supporta l'alternanza, le espressioni di parentesi negative, le parentesi e la stella di Kleene ed è in grado di ancorare all'inizio e alla fine della stringa, questo è tutto ciò che serve per questo approccio. Si noti tuttavia che gli insiemi negativi [^ ... ]
sono molto convenienti in aggiunta a quelli, perché altrimenti, è necessario sostituirli con un'espressione del modulo (a|b|c| ... )
che elenca tutti i caratteri che non sono nell'insieme, che è estremamente noioso e eccessivamente lungo, ancor più se l'intero set di caratteri è Unicode.
Con GNU grep
, la risposta sarebbe qualcosa del tipo:
grep "^\([^h]\|h\(h\|eh\|edh\)*\([^eh]\|e[^dh]\|ed[^eh]\)\)*\(\|h\(h\|eh\|edh\)*\(\|e\|ed\)\)$" input
(trovato con Graal e alcune ulteriori ottimizzazioni fatte a mano).
Puoi anche utilizzare uno strumento che implementa le espressioni regolari estese , come egrep
, per eliminare le barre rovesciate:
egrep "^([^h]|h(h|eh|edh)*([^eh]|e[^dh]|ed[^eh]))*(|h(h|eh|edh)*(|e|ed))$" input
Ecco uno script per testarlo (nota che genera un file testinput.txt
nella directory corrente):
#!/bin/bash
REGEX="^\([^h]\|h\(h\|eh\|edh\)*\([^eh]\|e[^dh]\|ed[^eh]\)\)*\(\|h\(h\|eh\|edh\)*\(\|e\|ed\)\)$"
# First four lines as in OP's testcase.
cat > testinput.txt <<EOF
hoho
hihi
haha
hede
h
he
ah
head
ahead
ahed
aheda
ahede
hhede
hehede
hedhede
hehehehehehedehehe
hedecidedthat
EOF
diff -s -u <(grep -v hede testinput.txt) <(grep "$REGEX" testinput.txt)
Nel mio sistema stampa:
Files /dev/fd/63 and /dev/fd/62 are identical
come previsto.
Per coloro che sono interessati ai dettagli, la tecnica utilizzata è quella di convertire l'espressione regolare che corrisponde alla parola in un automa finito, quindi invertire l'automa cambiando ogni stato di accettazione in non accettazione e viceversa, quindi riconvertendo il FA risultante in un'espressione regolare.
Infine, come tutti hanno notato, se il tuo motore di espressione regolare supporta lookahead negativo, questo semplifica molto l'attività. Ad esempio, con GNU grep:
grep -P '^((?!hede).)*$' input
Aggiornamento: Di recente ho trovato l'eccellente libreria FormalTheory di Kendall Hopkins , scritta in PHP, che offre una funzionalità simile a Grail. Usandolo e un semplificatore scritto da me stesso, sono stato in grado di scrivere un generatore online di espressioni regolari negative con una frase di input (solo caratteri alfanumerici e spaziali attualmente supportati): http://www.formauri.es/personal/ pgimeno / misc / non-match-regex /
Per hede
esso produce:
^([^h]|h(h|e(h|dh))*([^eh]|e([^dh]|d[^eh])))*(h(h|e(h|dh))*(ed?)?)?$
che equivale a quanto sopra.
([^h]*(h([^e]|$)|he([^d]|$)|hed([^e]|$)))*
:? L'idea è semplice Continua la corrispondenza finché non vedi l'inizio della stringa indesiderata, quindi esegui la corrispondenza solo nei casi N-1 in cui la stringa è incompleta (dove N è la lunghezza della stringa). Questi casi N-1 sono "h seguito da non-e", "seguito da non-d" e "hed seguito da non-e". Se sei riuscito a passare questi N-1 dei casi, è successo non ha corrispondono con la stringa indesiderata in modo da poter iniziare la ricerca di[^h]*
nuovo