awk Wiederholung {n} funktioniert nicht

18

Ich versuche, die Zeilen mit dem Wiederholungssymbol {n} zu drucken, aber es funktioniert nicht. Zum. Ich möchte zB alle Zeilen drucken, deren Länge 4 Zeichen lang ist

 awk '/^.{4}$/' test_data

Mit dem obigen Code wird das nicht gedruckt. Wie kann ich das Problem beheben, damit ich das Wiederholungssymbol verwenden kann? Ich kenne die Alternative wie awk '/^....$/' test_dataundawk 'length ==3 ' test_data

Für immer Lerner
quelle
3
Welche Distribution benutzt du? Welche awk?
Terdon
1
$ awk --version GNU Awk 3.1.7 $ cat / etc / redhat-release Red Hat Enterprise Linux Server Version 6.7 (Santiago)
Für immer Anfänger
2
Ich würde sagen awk '/^.{4}+$/{print}' <<<$'foods\nbaarsz\nfooo' , genau 4 Zeichen entsprechen. Auch wie Sie selbst erwähnt haben, awk 'length($0) == 4' test_dataist es mit fast allen awkVersionen kompatibel .
Valentin Bajrami
4
Machen awk --re-interval '/^.{4}$/' test_data oder awk --posix '/^.{4}$/' test_dataarbeiten
Steeldriver
Danke Stahlfahrer. Dies löste mein Problem. Upvoted.
Nochmals vielen

Antworten:

19

Gemäß dem GNU Awk-Benutzerhandbuch: Funktionsverlauf wurde die Unterstützung für Bereichsoperatoren für reguläre Ausdrücke in Version 3.0 hinzugefügt, erforderte jedoch anfangs eine explizite Befehlszeilenoption

Neue Befehlszeilenoptionen:

  • Neue Befehlszeilenoptionen:
    • Die Option --lint-old, um vor Konstrukten zu warnen, die in der ursprünglichen Version 7 Unix-Version von awk nicht verfügbar sind (siehe V7 / SVR3.1).
    • Die Option -m von BWK awk. (Brian war zu der Zeit noch in den Bell Laboratories.) Dies wurde später sowohl von seiner awk als auch von gawk entfernt.
    • Die Option --re-interval, um Intervallausdrücke in regulären Ausdrücken bereitzustellen (siehe Regexp-Operatoren).
    • Die Option --traditional wurde als besserer Name für --compat hinzugefügt (siehe Optionen).

In gawk4.0

Intervallausdrücke wurden Teil der regulären Standardausdrücke

Da Sie gawk3.x verwenden, müssen Sie verwenden

awk --re-interval '/^.{4}$/'

oder

awk --posix '/^.{4}$/'

oder (danke @ StéphaneChazelas) wenn Sie eine Lösung suchen, die portabel ist, verwenden Sie

POSIXLY_CORRECT=anything awk '/^.{4}$/'

(da --posixoder --re-intervalwürde einen Fehler in anderen awkImplementierungen verursachen).

Stahlfahrer
quelle
Danke steeldriver, für deine Zeit und Hilfe. Positiv bewertet und als Antwort angenommen
Für immer Anfänger
4
Es ist besser, POSIXLY_CORRECT=anything awk '/^.{4}/'portablen Code zu verwenden ( --posixoder --re-intervalwürde in anderen awkImplementierungen einen Fehler verursachen ).
Stéphane Chazelas
Hallo Stéphane Chazelas, als ich den Befehl $ POSIXLY_CORRECT = anything awk '/^.{4}/' test_data ausgab, wurden alle Zeilen gedruckt. Dann wurde mir klar, dass es nach Wiederholungen keinen letzten Dollar mehr gibt. Vielen Dank für Ihre Eingaben. Erweitere deinen Kommentar und deine Lösung. Entschuldigung, ich habe es in erster Linie falsch verstanden, weil das $ nach der Wiederholung weggelassen wurde.
Für immer Anfänger
20

EREs ( erweiterte reguläre Ausdrücke, wie sie von awkoder verwendet werden egrep) hatten anfangs keine {x,y}. Es wurde zuerst in BREs eingeführt (wie von grepoder verwendet sed), jedoch mit einer \{x,y\}Syntax, die die Rückwärtsportabilität nicht beeinträchtigt.

Als es jedoch mit dieser {x,y}Syntax zu EREs hinzugefügt wurde, brach es die Rückwärtsportabilität, da ein foo{2}RE zuvor mit etwas anderem übereinstimmte.

Einige Implementierungen haben sich daher dagegen entschieden. Sie werden feststellen , dass /bin/awk, /bin/nawkund /bin/egrepauf Solaris ehren immer noch nicht (man braucht verwenden /usr/xpg4/bin/awkoder /usr/xpg4/bin/grep -E). Gleich für awkund nawkauf FreeBSD (basierend auf dem awkvon Brian Kernighan (the kin awk) gepflegten ).

Für GNUawk mussten Sie es bis vor relativ kurzer Zeit (Version 4.0) mit aufrufen, POSIXLY_CORRECT=anything awk '/^.{4}$/'um es zu ehren. mawkehrt es immer noch nicht .

Beachten Sie, dass dieser Operator nur syntaktischer Zucker ist. .{3,5}kann ....?.?zum Beispiel immer geschrieben werden (obwohl natürlich {3,5}viel besser lesbar ist und das Äquivalent von (foo.{5,9}bar){123,456}viel schlechter wäre).

Stéphane Chazelas
quelle
Nochmals vielen Dank, Stéphane Chazelas. Entschuldigung, leider konnte ich Ihre Antwort anfangs nicht nachvollziehen. Vielen Dank und upvoted.
Für immer Anfänger
6

Dies funktioniert wie erwartet mit GNU awk(gawk):

$ printf 'abcd\nabc\nabcde\n' | gawk '/^.{4}$/'
abcd

Aber scheitert mit mawkdem näher an POSIX awkund, AFAIK, ist der Standard auf Ubuntu-Systemen:

$ printf 'abcd\nabc\nabcde\n' | mawk '/^.{4}$/'
$ ## prints nothing

Eine einfache Lösung wäre also die Verwendung gawkvon awk. Die {n}Notation ist nicht Teil der POSIX BRE-Syntax (Basic Regular Expression). Deshalb grepscheitert auch hier:

$ printf 'abcd\nabc\nabcde\n' | grep '^.{4}$'
$

Es ist jedoch Teil von ERE (Extended Regular Expressions):

$ printf 'abcd\nabc\nabcde\n' | grep -E '^.{4}$'
abcd

Ich weiß nicht, welches Regex-Aroma von mawkPOSIX verwendet wird awk, aber ich denke, es ist BRE. Sie verwenden eine ältere Version von ERE gemäß Stéphanes Antwort . Entweder verwenden Sie anscheinend eine Version awk, die ERE nicht implementiert, oder Ihre Eingabe enthält tatsächlich keine Zeilen mit genau 4 Zeichen. Dies kann beispielsweise an Leerzeichen liegen, die Sie nicht sehen, oder an Unicode-Glyphen.

terdon
quelle
Hallo terdon, ich möchte die Zeilen drucken, die 4 Zeichen lang sind. Nicht die ersten vier Zeichen einer Zeile. Zum Beispiel funktioniert $ grep -E '^. {4} $' test_data, aber dasselbe funktioniert nicht mit awk
Forever Learner
@CppLearner ja, das mache ich hier. Was meinst du?
Terdon
Die Lösung von @CppLearner, @ terdon druckt nur Zeilen mit einer Länge von 4 Zeichen. Aber wenn Sie wirklich nur an der Zeilenlänge interessiert sind, sollten Sie nur verwenden, length($0)was effizienter ist als reguläre Ausdrücke.
Stephen Kitt
Hallo terdon, die Lösung von steeldriver ist genau das, wonach ich gesucht habe. Vielen Dank für Ihre Zeit. Hallo Stephen Kitt, Wie ich im Problem erwähnt habe, habe ich die Länge bereits als Alternative verwendet. Ich war mehr daran interessiert zu wissen, warum die Wiederholungs-Regex {n} nicht aus dem Kommentar von steeldriver hervorgeht. Ich habe erfahren, dass ich die Option von verwenden muss --re-interval oder --posix. Vielen Dank für Ihre Zeit.
Für immer Anfänger
1
mawkist nicht wirklich näher an POSIX awkund verwendet keine BREs. Es werden EREs verwendet, jedoch ohne den {x,y}Operator.
Stéphane Chazelas