Befehl grep, um alle Zeilen anzuzeigen, die mit demselben Zeichen beginnen und enden


8

Ich möchte wissen, wie man verwendet grep, um alle Zeilen anzuzeigen, die mit demselben Zeichen beginnen und enden.

Antworten:


14

POSIXly:

pattern='\(.\).*\1
.'
grep -x -- "$pattern" file

Es funktioniert nicht, wenn die Zeile mit einem ungültigen Bytezeichen beginnt oder endet. Wenn Sie diesen Fall abdecken möchten, können Sie hinzufügen LC_ALL=C, obwohl dies nur LC_ALL=Cmit Einzelbyte-Zeichendaten funktioniert.


perl6 scheint das beste Werkzeug zu sein, wenn Sie es in Ihrer Box haben:

$ printf '\ue7\u301 blah \u107\u327\n121\n1\n123\n' |
  perl6 -ne '.say if m/^(.).*$0$/ || /^.$/'
ḉ blah ḉ
121
1

Obwohl es immer noch an ungültigen Zeichen erstickt.


Beachten Sie, dass perl6sich Ihr Text ändert, indem Sie ihn in eine NFCForm verwandeln :

$ printf '\u0044\u0323\u0307\n' |
  perl6 -pe ''                  |
  perl -CI -ne 'printf "U+%04x\n", ord for split //'
U+1e0c
U+0307
U+000a

$ printf '\u0044\u0323\u0307\n' |
  perl -pe ''                   |
  perl -CI -ne 'printf "U+%04x\n", ord for split //'
U+0044
U+0323
U+0307
U+000a

Intern perl6speichert Zeichenfolge in NFGForm (steht für Normalization Form Grapheme), die perl6erfunden wurde, um mit nicht vorkomponierten Graphemen richtig umzugehen:

$ printf '\u0044\u0323\u0307\n' | perl6 -ne '.chars.say'
1
$ printf '\u0044\u0323\u0307\n' | perl6 -ne '.codes.say'
2

2
Perls Umgang mit Unicode-Text ist geradezu vorbildlich, so dass viele "einfache" Aufgaben in Perl mit anderen Tools praktisch nicht implementiert werden können, zumindest nicht mit der gleichen Genauigkeit.
Dietrich Epp

1
Es sollte jedoch beachtet werden, dass perl6der Text dadurch geändert wird (drehen Sie ihn in NFC (Normalisierungsformular "zusammengesetzt")).
Stéphane Chazelas

@ StéphaneChazelas: Ja, fairer Punkt. Beachten Sie auch, dass die Zeichenfolge in perl6in NFGForm ( Gfür Grapheme) gespeichert wird perl6, um mit nicht vorkomponierten Graphemen richtig umzugehen.
Cuonglm

10

Nicht grep aber awk:

awk -F "" 'NF && $1 == $NF'

Diese Sonderfälle werden behandelt:

  • Es werden keine leeren Zeilen gedruckt
  • Es werden immer 1-stellige Zeilen gedruckt

Ein leerer FS teilt den Datensatz in ein Zeichen pro Feld gawk, mawkund busybox awk(Bytes, keine Zeichen für die letzteren zwei), ist aber nicht Standard und funktioniert nicht in den Implementierungen der awkvon der ursprünglichen , abgeleitet durch A, W und K wie auf BSDs und kommerziellen Einheiten. Tragbarer, aber mehr zu tippen:

awk '/./ && substr($0,1,1) == substr($0,length)'

1
Beachten Sie, dass FSeine leere Zeichenfolge kein Standard ist und in einigen awkImplementierungen nicht funktioniert .
Cuonglm

2
Alternative, die das Teilen vermeidet und vollständig portierbar ist (selbst für das maximal schreckliche Solaris 'alte' awk) awk 'length&&substr($0,1,1)==substr($0,length)'(Standardargument von lengthis beachten $0und Standardaktion ist {print $0})
dave_thompson_085

@ dave_thompson_085: thx, ich verwende nur Ihren Standardaktionshinweis, um den kürzesten Befehl zu haben.
Rudimeier

Firne. Eine kleine Korrektur; Mein Test für Solaris old awk war falsch (ich hatte versehentlich xpg4 aktiviert), aber diese Methode funktioniert, nawkdie fast genauso schlecht ist :-)
dave_thompson_085

8
grep -xe '\(.\).*\1' -e .

Beispiel:

$ printf '%s\n' il y était cet été  | grep -xe '\(.\).*\1' -e .
y
été

-xist für die genaue Übereinstimmung (Übereinstimmung auf der gesamten Linie). \1als Rückverweis auf den in \(.\). Wir fügen ein hinzu -e ., um den Sonderfall einer Zeile mit einem einzelnen Zeichen zu behandeln.

Es wird davon ausgegangen, dass die Eingabe gültigen Text im aktuellen Gebietsschema enthält.

Das Spiel ist auf Zeichen , nicht - Byte (die é in UTF-8 sind die zwei Bytes 0xC3 0xA9 zum Beispiel), noch Graphem - Cluster (es würde nicht funktionieren , wenn diejenigen E wurden in ihrem zerlegten Form geschrieben mit egefolgt von der U + 0301 zum Beispiel akuten Akzent kombinieren).

So arbeiten Sie an Graphemclustern mit einem grep, der -PPCRE unterstützt :

$ printf 'e\u0301te\u0301\n' | grep -xPe '(\X).*\1|\X'
été

Dies setzt voraus, dass die Zerlegung für die beiden Cluster gleich ist, zum Beispiel ein Ausdruck, c U+0301 U+0327der nicht mit einem übereinstimmt, der als c U+0327 U+0301oder ć( U+0107) U+0327oder ç( U+00E7) U+0301oder ḉ ( U+1E09) ausgedrückt wird . Dazu müssten Sie die Prüfung auf einem normalisierten Formular durchführen:

$ printf '\ue7\u301 blah \u107\u327\n' |
  perl -MUnicode::Normalize -C -ne '
    print if /^\X$/ || NFC($_) =~ /^(\X).*\1$/'
ḉ blah ḉ

1
Wenn Sie haben perl6, dann perl6 -ne '.say if m/^(.).*$0$/ || /^.$/'sollten Sie alle Arbeiten für Sie erledigen.
Cuonglm

1

Schnelle Python2-Alternative:

python -c 'import sys;[sys.stdout.write(l) for l in sys.stdin if len(l)>1 and l.rstrip("\n").endswith(l[0])]' < input.txt

Beispiel:

$ python -c 'import sys;[sys.stdout.write(l) for l in sys.stdin if len(l)>1 and l.rstrip("\n").endswith(l[0])]' < input.txt  | cat -A 
nathan$
 ookie $
a line a$

Es ist fehlgeschlagen, wenn die Zeile nachgestellte oder führende Leerzeichen enthält, z. B. "121".
Cuonglm

@ Cuonglm das stimmt. Aber war nachfolgendes oder führendes Leerzeichen eine Voraussetzung? Dies erledigt die angeforderte Aufgabe - prüfen Sie, ob das führende und das letzte Zeichen identisch sind. Whitespace ist immer noch ein ASCII-Zeichen, nein?
Sergiy Kolodyazhnyy

@ Cuonglm Ihre scheiterte übrigens auch mit nachlaufendem und führendem Platz :)
Sergiy Kolodyazhnyy

Ihr Code entfernt führende und nachfolgende Leerzeichen, sodass die Eingabezeile geändert wird. Es gibt auch einen Fehler für leere Zeilen.
Rudimeier

@Serg: Wie? Meine Antwort ist nur greifend, sie ändert die Eingabe nicht.
Cuonglm
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.