awk repetition {n} nie działa

18

Próbuję wydrukować linie za pomocą symbolu powtórzenia {n}, ale to nie działa. Dla. np. chcę wydrukować wszystkie linie o długości 4 znaków

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

Powyższy kod nie drukuje tego .Jak to naprawić, aby móc użyć symbolu powtórzenia? Znam alternatywę jak awk '/^....$/' test_dataiawk 'length ==3 ' test_data

Forever Learner
źródło
3
Jakiej dystrybucji używasz? Który awk?
terdon
1
$ awk --version GNU Awk 3.1.7 $ cat / etc / redhat-release Red Hat Enterprise Linux Server wersja 6.7 (Santiago)
Forever Learner
2
Powiedziałbym, awk '/^.{4}+$/{print}' <<<$'foods\nbaarsz\nfooo' żeby dopasować dokładnie 4 znaki. Jak już wspomniałeś, awk 'length($0) == 4' test_datajest kompatybilny z prawie wszystkimi awkwersjami.
Valentin Bajrami
4
Zrobić awk --re-interval '/^.{4}$/' test_data lub awk --posix '/^.{4}$/' test_datapracy?
steeldriver
Dziękuję steeldriver. To rozwiązało mój problem. Pozytywne. Jeszcze raz dziękuję :)
Forever Learner

Odpowiedzi:

19

Zgodnie z Podręcznikiem użytkownika GNU Awk: Historia funkcji , obsługa operatorów zakresu wyrażeń regularnych została dodana w wersji 3.0, ale początkowo wymagała jawnej opcji wiersza poleceń

Nowe opcje wiersza polecenia:

  • Nowe opcje wiersza polecenia:
    • Opcja --lint-old ostrzegająca o konstrukcjach, które nie są dostępne w oryginalnej wersji awk w wersji 7 Uniksa (patrz V7 / SVR3.1).
    • Opcja -m z BWK awk. (Brian był wtedy w Bell Laboratories.) Zostało to później usunięte zarówno z jego awk, jak i z gawk.
    • Opcja --re-Interwał zapewniająca wyrażenia interwałowe w wyrażeniach regularnych (zobacz Operatory Regexp).
    • Opcja --traditional została dodana jako lepsza nazwa dla --compat (patrz Opcje).

W gawk4.0

Wyrażenia przedziałowe stały się częścią domyślnych wyrażeń regularnych

Ponieważ używasz gawk3.x, będziesz musiał użyć

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

lub

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

lub (dzięki @ StéphaneChazelas), jeśli chcesz mieć rozwiązanie przenośne, użyj

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

(ponieważ --posixlub --re-intervalspowodowałby błąd w innych awkimplementacjach).

steeldriver
źródło
Dzięki steeldriver, za poświęcony czas i pomoc. Głosowano i zaakceptowano jako odpowiedź
Forever Learner
4
Lepiej jest użyć, POSIXLY_CORRECT=anything awk '/^.{4}/'ponieważ tworzy przenośny kod (a --posixlub --re-intervalspowodowałby błąd w innych awkimplementacjach).
Stéphane Chazelas
Cześć Stéphane Chazelas, kiedy wydałem polecenie $ POSIXLY_CORRECT = cokolwiek awk '/^.{4}/' test_data, wypisał wszystkie linie. Potem zdałem sobie sprawę, że po powtórzeniach nie ma ostatniego dolara. Dziękuję za twoje uwagi. Poprawienie komentarza i rozwiązania. Przepraszam, źle to zrozumiałem z powodu pominięcia $ po powtórzeniu.
Forever Learner
20

ERE ( rozszerzone wyrażenia regularne używane przez awklub egrep) początkowo nie miały {x,y}. Po raz pierwszy został wprowadzony w BRE (używanych przez greplub sed), ale ze \{x,y\}składnią, która nie zepsuła przenośności wstecznej.

Ale kiedy został dodany do ERE z tą {x,y}składnią, przerwał przenośność wsteczną, ponieważ foo{2}RE dopasowywał wcześniej coś innego.

Dlatego niektóre implementacje zdecydowały się tego nie robić. Znajdziesz to /bin/awk, /bin/nawka /bin/egrepna Solarisie nadal nie honoruj ​​tego (musisz użyć /usr/xpg4/bin/awklub /usr/xpg4/bin/grep -E). Sama dla awki nawkna FreeBSD (na podstawie utrzymane przez Brian Kernighana (The w )).awkkawk

W przypadku GNUawk do niedawna (wersja 4.0) trzeba było do niego zadzwonić, POSIXLY_CORRECT=anything awk '/^.{4}$/'aby go uhonorować. mawkwciąż tego nie szanuje .

Zauważ, że ten operator to tylko cukier syntaktyczny. .{3,5}zawsze można napisać ....?.?na przykład (choć oczywiście {3,5}jest o wiele bardziej czytelny, a odpowiednik (foo.{5,9}bar){123,456}byłby o wiele gorszy).

Stéphane Chazelas
źródło
Jeszcze raz dziękuję Stéphane Chazelas. Przepraszam, niestety na początku nie mogłem zrozumieć twojej odpowiedzi. Bardzo dziękuję i głosowałem.
Forever Learner
6

Działa to zgodnie z oczekiwaniami w przypadku GNU awk(gawk):

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

Ale kończy się niepowodzeniem, mawkco jest bliższe POSIX, awka AFAIK jest ustawieniem domyślnym w systemach Ubuntu:

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

Zatem prostym rozwiązaniem byłoby użycie gawkzamiast awk. {n}Notacja nie jest częścią składni POSIX BRE (podstawowe wyrażenie regularne). Dlatego grepteż tutaj zawodzi:

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

Jest jednak częścią ERE (rozszerzone wyrażenia regularne):

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

Nie wiem, jakiego smaku wyrażenia regularnego używa mawkPOSIX awk, ale zgaduję, że to BRE. Używają starszej wersji ERE zgodnie z odpowiedzią Stéphane'a . W każdym razie albo najwyraźniej używasz wersji awk, która nie implementuje ERE lub twoje dane wejściowe nie zawierają żadnych wierszy zawierających dokładnie 4 znaki. Może się to zdarzyć na przykład z powodu niewidocznych białych znaków lub glifów Unicode.

terdon
źródło
Cześć Terdon, chcę wydrukować linie o długości 4 znaków. Nie pierwsze cztery znaki linii. Na przykład $ grep -E '^. {4} $' test_data, zadziała, ale to samo nie działa z awk
Forever Learner
@CppLearner tak, właśnie to robię tutaj. Co masz na myśli?
terdon
@CppLearner, rozwiązanie @ terdon drukuje tylko linie o długości 4 znaków. Ale jeśli naprawdę interesuje Cię tylko długość linii, powinieneś po prostu użyć tej, length($0)która jest bardziej wydajna niż wyrażenia regularne.
Stephen Kitt
Cześć Terdon, rozwiązanie steeldriver było tym, czego szukałem. Dziękuję za Twój czas. Cześć Stephen Kitt, jak wspomniałem w tym problemie, użyłem już długości jako alternatywy, byłem bardziej zainteresowany tym, dlaczego wyrażenie regularne {n} nie działa z komentarza steeldrivera. Dowiedziałem się, że muszę skorzystać z opcji --re-odstęp lub --posix. Dziękuję za Twój czas.
Forever Learner
1
mawknie jest tak naprawdę bliżej POSIX awki nie używa BRE. Używa ERE, ale bez {x,y}operatora.
Stéphane Chazelas