Как выполнить grep для групп из n цифр, но не более n?

Как выполнить grep для групп из n цифр, но не более n?

Я изучаю Linux, и у меня есть задача, которую я, похоже, не могу решить самостоятельно. Вот она:

найти строку из файла, содержащую 4 числа подряд, но не более 4.

Я не уверен, как к этому подойти. Я могу искать конкретные числа, но не их сумму в строке.

решение1

Есть два способа интерпретировать этот вопрос; я рассмотрю оба случая. Вы можете захотеть отобразить строки:

  1. которые содержат последовательность из четырех цифр, которая сама по себе не является частью какой-либо более длинной последовательности цифр,или
  2. который содержит последовательность из четырех цифр, но больше не является последовательностью цифр (даже по отдельности).

Например, (1) отобразит 1234a56789, а (2) — нет.


Если вы хотите отобразить все строки, содержащие последовательность из четырех цифр, которая сама по себе не является частью какой-либо более длинной последовательности цифр, один из способов:

grep -P '(?<!\d)\d{4}(?!\d)' file

Это используетРегулярные выражения Perl, который в Ubuntugrep(GNU-команда grep) поддерживает через -P. Он не будет соответствовать тексту типа 12345, а также не будет соответствовать 1234или 2345, которые являются его частью.Но он будет соответствовать 1234в 1234a56789.

В регулярных выражениях Perl:

  • \dозначает любую цифру (это сокращенный вариант « [0-9]или [[:digit:]]»).
  • x{4}Матчиx4 раза. ( { }синтаксис не является специфичным для регулярных выражений Perl; он grep -Eтакже присутствует в расширенных регулярных выражениях via.) So \d{4}то же самое, что и \d\d\d\d.
  • (?<!\d)— это отрицательное утверждение с просмотром назад нулевой ширины. Оно означает «если не предшествует \d».
  • (?!\d)— это отрицательное утверждение с нулевой шириной. Оно означает «если не следует \d».

(?<!\d)и (?!\d)не сопоставляют текст за пределами последовательности из четырех цифр; вместо этого они (при совместном использовании) предотвращают сопоставление последовательности из четырех цифр, если она является частью более длинной последовательности цифр.

Использование только просмотра назад или только просмотра вперед недостаточно, поскольку крайняя правая или крайняя левая четырехзначная подпоследовательность все равно будет сопоставлена.

Одно из преимуществ использованияутверждения о взгляде назад и взгляде впередзаключается в том, что ваш шаблон соответствует только самим четырехзначным последовательностям, а не окружающему тексту. Это полезно при использовании цветного выделения (с опцией --color).

ek@Io:~$ grep -P '(?<!\d)\d{4}(?!\d)' <<< 12345abc789d0123e4
12345abc789d0123e4

По умолчаниюВ Ubuntu каждый пользователь имеет alias grep='grep --color=auto'в своем распоряжении~.bashrcфайл. Таким образом, вы автоматически получаете цветное выделение, когда запускаете простую команду, начинающуюся с grep(это когдапсевдонимырасширены) истандартный выводявляетсятерминал(Это то, что--color=autoпроверяет). Совпадения обычно выделяются оттенком красного (близким ккиноварь), но я выделил его жирным курсивом.Вот скриншот:
Снимок экрана, показывающий команду grep с выводом 12345abc789d0123e4, где 0123 выделено красным.

И вы даже можете сделать grepтак, чтобы был напечатан только соответствующий текст, а не вся строка, с помощью -o:

ek@Io:~$ grep -oP '(?<!\d)\d{4}(?!\d)' <<< 12345abc789d0123e4
0123

Альтернативный путь,БезУтверждения о взгляде назад и взгляде вперед

Однако, если вы:

  1. нужна команда, которая также будет работать в системах, где grepне поддерживаются -Pили по иным причинам не хотят использоваться регулярные выражения Perl,и
  2. не нужно специально сопоставлять четыре цифры — что обычно и происходит, если ваша цель — просто отобразить строки, содержащие совпадения,и
  3. согласны с решением, которое немного менее элегантно

...тогда вы можете добиться этого с помощьюрасширенное регулярное выражениевместо:

grep -E '(^|[^0-9])[0-9]{4}($|[^0-9])' file

Это соответствует четырем цифрам и нецифровому символу — или началу или концу строки — вокруг них. А именно:

  • [0-9]соответствует любой цифре (например [[:digit:]], или \dв регулярных выражениях Perl) и {4}означает «четыре раза». То есть [0-9]{4}соответствует последовательности из четырех цифр.
  • [^0-9]соответствует символам, не входящим в диапазон от 0до 9. Это эквивалентно [^[:digit:]](или \D, в регулярных выражениях Perl).
  • ^, когда он не указан в [ ]скобках, соответствует началу строки. Аналогично, $соответствует концу строки.
  • |означаетилии скобки для группировки (как в алгебре). Так (^|[^0-9])соответствует началу строки или нецифровому символу, а ($|[^0-9])соответствует концу строки или нецифровому символу.

Таким образом, совпадения возникают только в строках, содержащих четырехзначную последовательность ( [0-9]{4}), которая одновременно:

  • в начале строки или с предшествующим ему символом, не являющимся цифрой ( (^|[^0-9])),и
  • в конце строки или за ним следует нецифровой символ ( ($|[^0-9])).

Если, с другой стороны, вы хотите отобразить все строки, которые содержат четырехзначную последовательность, но не содержатлюбойпоследовательность из более чем четырех цифр (даже если она отделена от другой последовательности, состоящей всего из четырех цифр), то концептуально ваша цель — найти строки, которые соответствуют одному шаблону, но не соответствуют другому.

Поэтому, даже если вы знаете, как это сделать с помощью одного шаблона, я бы посоветовал использовать что-то вродеМэттвторое предложение, greping для двух шаблонов по отдельности.

Вы не получите особой выгоды от каких-либо расширенных возможностей регулярных выражений Perl, делая это, поэтому вы можете предпочесть не использовать их. Но в соответствии с вышеуказанным стилем, вот сокращениерешение Мэттаиспользуя \d(и фигурные скобки) вместо [0-9]:

grep -P '\d{4}' file | grep -Pv '\d{5}'

Так как он использует [0-9],путь Мэттаболее переносима -- она ​​будет работать на системах, где grepне поддерживаются регулярные выражения Perl. Если вы используете [0-9](или [[:digit:]]) вместо \d, но продолжаете использовать { }, вы получаете переносимость способа Мэтта немного более лаконично:

grep -E '[0-9]{4}' file | grep -Ev '[0-9]{5}'

Альтернативный способ, с единым узором

Если вы действительно предпочитаете grepкоманду, которая

  1. использует одно регулярное выражение(не две буквы greps, разделенные знакомтрубка, как указано выше)
  2. для отображения строк, содержащих хотя бы одну последовательность из четырех цифр,
  3. но нет последовательностей из пяти (или более) цифр,
  4. и вы не против совпадения всей строки, а не только цифр (вероятно, вас это не смущает)

...тогда вы можете использовать:

grep -Px '(\d{0,4}\D)*\d{4}(\D\d{0,4})*' file

Флаг -xотображает grepтолько те строки, в которых совпадает вся строка (а не любая строкасодержащийматч).

Я использовал регулярное выражение Perl, потому что считаю, что краткость \dи \Dсущественно повышает ясность в этом случае. Но если вам нужно что-то переносимое на системы, где grepне поддерживается -P, вы можете заменить их на [0-9]и [^0-9](или на [[:digit:]]и [^[:digit]]):

grep -Ex '([0-9]{0,4}[^0-9])*[0-9]{4}([^0-9][0-9]{0,4})*' file

Вот как работают эти регулярные выражения:

  • В середине, \d{4}или [0-9]{4}соответствует одной последовательности из четырех цифр. У нас может быть больше одного из них, но нам нужно иметь по крайней мере один.

  • Слева (\d{0,4}\D)*or ([0-9]{0,4}[^0-9])*соответствует нулю или более ( *) экземпляров не более четырех цифр, за которыми следует нецифра. Ноль цифр (т.е. ничего) — одна из возможностей для «не более четырех цифр». Это соответствует(а)пустая строка или(б)любая строкаокончаниев нецифровом формате и не содержащем последовательностей из более чем четырех цифр.

    Поскольку текст, расположенный непосредственно слева от центрального символа \d{4}(или [0-9]{4}), должен быть либо пустым, либо заканчиваться нецифрой, это не позволяет центральному символу \d{4}сопоставлять четыре цифры, слева от которых находится еще одна (пятая) цифра.

  • Справа (\D\d{0,4})*or ([^0-9][0-9]{0,4})*соответствует нулю или более ( *) экземпляров нецифры, за которыми следует не более четырех цифр (которые, как и прежде, могут быть четырьмя, тремя, двумя, одной или даже ни одной). Это соответствует(а)пустая строка или(б)любая строканачалов нецифровом формате и не содержащем последовательностей из более чем четырех цифр.

    Поскольку текст, расположенный непосредственно справа от центрального символа \d{4}(или [0-9]{4}), должен быть либо пустым, либо начинаться с символа, отличного от цифры, это не позволяет центральному символу \d{4}сопоставлять четыре цифры, справа от которых находится еще одна (пятая) цифра.

Это гарантирует, что где-то присутствует последовательность из четырех цифр и что нигде не присутствует последовательность из пяти или более цифр.

Это не плохо и не неправильно делать это таким образом. Но, возможно, самая важная причина рассмотреть эту альтернативу заключается в том, что она проясняет выгоду использования (или аналогичного) вместо этого, как предложено выше и вgrep -P '\d{4}' file | grep -Pv '\d{5}'ответ Мэтта.

При таком подходе становится ясно, что ваша цель — выбрать строки, содержащие одно, но не другое. Плюс синтаксис проще (поэтому его могут быстрее понять многие читатели/сопровождающие).

решение2

Это покажет вам 4 числа подряд, но не больше.

grep '[0-9][0-9][0-9][0-9][^0-9]' file

Обратите внимание, что ^ означает «нет».

Есть проблема, хотя я не уверен, как ее исправить... если номер является концом строки, то он не будет отображаться.

Однако эта более уродливая версия подойдет для этого случая.

grep '[0-9][0-9][0-9][0-9]' file | grep -v [0-9][0-9][0-9][0-9][0-9]

решение3

Вы можете попробовать следующую команду, заменив fileее фактическим именем файла в вашей системе:

grep -E '(^|[^0-9])[0-9]{4}($|[^0-9])' file

Вы также можете проверитьэтот урокдля получения дополнительных сведений о применении команды grep.

решение4

Если grepрегулярные выражения Perl ( ) не поддерживаются -P, используйте следующую команду оболочки:

grep -w "$(printf '[0-9]%.0s' {1..4})" file

где printf '[0-9]%.0s' {1..4}даст 4 раза [0-9]. Этот метод полезен, когда у вас длинные цифры и вы не хотите повторять шаблон (просто замените 4на свое число ваши цифры для поиска).

Использование -wбудет искать целые слова. Однако, если вас интересуют буквенно-цифровые строки, такие как 1234a, то добавьте [^0-9]в конец шаблона, например

grep "$(printf '[0-9]%.0s' {1..4})[^0-9]" file

Использование $()в основном являетсязамена команды. Проверь этопочтачтобы увидеть, как printfповторяется узор.

Связанный контент