Mustervergleich schließen doppelte Zeichen aus

7

Gibt es einen regulären Ausdruck für Folgendes, der Zeichen in einem Zeichensatz nur einmal entspricht? Mit anderen Worten, sobald ein Zeichen gefunden wurde, entfernen Sie es aus dem Satz.

Wenn grep dies nicht kann, gibt es ein eingebautes Dienstprogramm, das dies kann?

Beispiel:

Characters to match only once:   spine

Eingang:

spine
spines
spin
pine
seep 
spins

Ausgabe:

spine
spin
pine

BEARBEITEN:
Es gibt viele Möglichkeiten, um diese Ausgabe zu erzielen (ein Beispiel unten), aber ich suche nach einer Möglichkeit, dies zu tun, ohne den Befehl für jedes Muster anpassen zu müssen, mit dem ich übereinstimmen möchte.

grep '[spine]' input_file | grep -v 's.*s' | ... | grep -v 'e.*e'

Steven
quelle
Frage: Was ist die Anwendung dafür?
Mdpc

Antworten:

4

Mit regulären Ausdrücken im mathematischen Sinne ist dies möglich, aber die Größe der regulären Ausdrücke wächst exponentiell im Verhältnis zur Größe des Alphabets, sodass dies nicht praktikabel ist.

Es gibt einen einfachen Weg mit Negation und Rückreferenzen .

grep '[spine]' | grep -Ev '([spine]).*\1'

Die ersten grepwählt Linien , die mindestens eines von enthält einps; Die zweite greplehnt Zeilen ab, die mehr als eine enthalten (z. B. Zulassen spinal tapund spendaber nicht foobaroder see).

Gilles 'SO - hör auf böse zu sein'
quelle
1

Inspiriert von Ihrem Gesichtsausdruck kann ich mit egrep einen kürzeren finden:

egrep -v '(s.*s|p.*p|i.*i|n.*n|e.*e)' FILE

das ist äquivalent zu

sed /s.*s/d;/p.*p/d;/i.*i/d;/n.*n/d;/e.*e/d; FILE

Und so erzeugt man den sed-Befehl automatisch aus der Eingabe:

#!/bin/bash
word=$1
file=$2
expr=$(for c in $(echo $word | sed 's/./& /g'); do echo -n "/"$c".*"$c"/d;"; done);
sed $expr $file 

Ich habe einen ähnlichen Ansatz mit grep versucht, konnte die Shell jedoch nicht davon überzeugen, das grep-Muster aus einer Variablen zu übernehmen. Wenn ich es jedoch wiedergab und das Ergebnis mit Ausschneiden und Einfügen einfügte, funktionierte der Befehl:

expr="'("$(for c in $(echo $wort | sed 's/./& /g'); do echo -n $c".*"$c"|"; done)

egrep -v ${expr/%|/)\'} FILE
# doesn't work, filters nothing, whole file is printed
# check:    
echo egrep -v $(echo $exp) FILE 
egrep -v '(s.*s|p.*p|i.*i|n.*n|e.*e)' FILE
# manually: 
egrep -v '(s.*s|p.*p|i.*i|n.*n|e.*e)' FILE
spine
spin
pine

Vielleicht habe ich einen Fehler gemacht, vielleicht habe ich einen Fehler mit der variablen Erweiterung gemacht.

Benutzer unbekannt
quelle
Die gewünschte Ausgabe finden Sie in meinem bearbeiteten Beitrag. Außerdem suche ich nach einer Lösung, die keinen komplexen, langwierigen, musterspezifischen Befehl erfordert.
Steven
Ja ich sehe. Vielleicht finde ich einen Weg, den sed-Befehl aus dem Wort 'Wirbelsäule' zu erzeugen.
Benutzer unbekannt
Endlich herausgefunden, wie man es mit sed löst - ist das akzeptabel?
Benutzer unbekannt