повторение awk {n} не работает

18

Я пытаюсь напечатать строки, используя символ повторения {n}, но это не работает. За. например, я хочу напечатать все строки длиной 4 символа

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

Приведенный выше код не печатает это. Как это исправить, чтобы я мог использовать символ повторения? Я знаю альтернативу, как awk '/^....$/' test_dataиawk 'length ==3 ' test_data

Навсегда ученик
источник
3
Какой дистрибутив вы используете? Какой awk?
Terdon
1
$ awk --version GNU Awk 3.1.7 $ cat / etc / redhat-release Red Hat Enterprise Linux Server, версия 6.7 (Сантьяго)
ученик навсегда,
2
Я бы сказал, awk '/^.{4}+$/{print}' <<<$'foods\nbaarsz\nfooo' чтобы соответствовать ровно 4 символа. Также, как вы упомянули сами, awk 'length($0) == 4' test_dataсовместим практически со всеми awkверсиями.
Валентин Байрами,
4
Делать awk --re-interval '/^.{4}$/' test_data или awk --posix '/^.{4}$/' test_dataработать?
Стальной водитель
Спасибо, стилдрайвер. Это решило мою проблему. Upvoted.
Forever Learner

Ответы:

19

Согласно Руководству пользователя GNU Awk: История возможностей , поддержка операторов диапазона регулярных выражений была добавлена ​​в версии 3.0, но изначально требовался явный параметр командной строки.

Новые параметры командной строки:

  • Новые параметры командной строки:
    • Опция --lint-old для предупреждения о конструкциях, которые недоступны в исходной версии awk Unk версии 7 (см. V7 / SVR3.1).
    • Опция -m от BWK awk. (Брайан все еще был в лабораториях Белла в то время.) Это было позже удалено как из его awk, так и из gawk.
    • Опция --re-interval обеспечивает интервальные выражения в регулярных выражениях (см. Операторы регулярных выражений).
    • Опция --traditional была добавлена ​​в качестве лучшего имени для --compat (см. Параметры).

В gawk4.0

Интервальные выражения стали частью регулярных выражений по умолчанию

Поскольку вы используете gawk3.x, вам нужно будет использовать

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

или

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

или (спасибо @ StéphaneChazelas), если вы хотите портативное решение, используйте

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

(так как --posixили --re-intervalможет вызвать ошибку в других awkреализациях).

steeldriver
источник
Спасибо Steeldriver, за ваше время и помощь. Проголосовал и принял в качестве ответа
Forever Learner
4
Лучше использовать, POSIXLY_CORRECT=anything awk '/^.{4}/'поскольку это создает переносимый код ( --posixили --re-intervalможет вызвать ошибку в других awkреализациях).
Стефан
Привет, Стефан Шазелас, когда я выполнил команду $ POSIXLY_CORRECT = что-нибудь awk '/^.ndom4‹/' test_data, он напечатал все строки. Тогда я понял, что нет последнего доллара после повторов. Спасибо за ваш вклад. Upvoting ваш комментарий и решение. Извините, я неправильно понял это в первую очередь из-за пропуска $ после повторения.
Forever Learner
20

ERE ( расширенные регулярные выражения, используемые awkили egrep) изначально не имели {x,y}. Впервые он был введен в BRE (как используется grepили sed), но с \{x,y\}синтаксисом, который не нарушал обратной переносимости.

Но когда он был добавлен в ERE с этим {x,y}синтаксисом, он сломал обратную переносимость, так как foo{2}RE раньше соответствовал чему-то другому.

Поэтому некоторые реализации решили не делать этого. Вы найдете это /bin/awk, /bin/nawkи /bin/egrepв Solaris все еще не соблюдаете это (вы должны использовать /usr/xpg4/bin/awkили /usr/xpg4/bin/grep -E). То же самое для awkи nawkна FreeBSD ( на основе поддерживается Брайан Керниган (далее в )).awkkawk

Для GNUawk , до относительно недавнего времени (версия 4.0), вы должны были вызвать POSIXLY_CORRECT=anything awk '/^.{4}$/'его, чтобы почтить его. mawkвсе еще не соблюдает это .

Обратите внимание, что этот оператор является только синтаксическим сахаром. .{3,5}например, всегда может быть написано ....?.?(хотя, конечно {3,5}, это намного более разборчиво, и эквивалент (foo.{5,9}bar){123,456}будет намного хуже).

Стефан Шазелас
источник
Еще раз спасибо Стефан Chazelas. Извините, мой плохой, я не смог понять ваш ответ изначально. Большое спасибо и проголосовал.
Forever Learner
6

Это работает, как и ожидалось с GNU awk(gawk):

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

Но сбои, mawkкоторые ближе к POSIX awkи, по умолчанию, в системах Ubuntu, AFAIK:

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

Таким образом, простое решение будет использовать gawkвместо awk. {n}Обозначение не является частью синтаксиса POSIX BRE (базовое регулярное выражение). Вот почему grepтакже не удается здесь:

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

Однако это часть ERE (расширенные регулярные выражения):

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

Я не знаю, какой вкус регулярных выражений используется mawkPOSIX или POSIX awk, но я предполагаю, что это BRE, Они используют старую версию ERE в соответствии с ответом Стефана . В любом случае, либо вы, очевидно, используете версию awk, которая не реализует ERE, либо ваш ввод фактически не содержит строк с ровно 4 символами. Это может произойти, например, из-за пробелов, которые вы не видите, или из символов Unicode.

Тердон
источник
Привет, Тердон, я хочу напечатать строки длиной 4 символа. Не первые четыре символа строки. Например, $ grep -E '^. {4} $' test_data, будет работать, но то же самое не работает с awk
Forever Learner
@CppLearner да, это то, что я делаю здесь. Что вы имеете в виду?
Тердон
@CppLearner, решение @ terdon позволяет печатать только строки длиной 4 символа. Но если вас действительно интересует только длина строки, вам следует просто использовать length($0)более эффективные, чем регулярные выражения.
Стивен Китт
Привет, Тердон, решение стального водителя - это то, что я искал. Спасибо за ваше время. Привет, Стивен Китт. Как я уже упоминал в этой задаче, я уже использовал длину в качестве альтернативы, меня больше интересовало, почему повторное выражение {n} не работает из комментария Steeldriver. Я узнал, что мне нужно использовать опцию --re-interval или --posix. Спасибо за ваше время.
Forever Learner
1
mawkна самом деле не ближе к POSIX awkи не использует BRE. Он использует ERE, но без {x,y}оператора.
Стефан