Как к grep для групп n цифр, но не больше, чем n?

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

grep строка из файла, который содержит 4 числа подряд, но не больше чем 4.

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

33
задан 18 October 2014 в 21:05

4 ответа

Существует два способа интерпретировать этот вопрос; я обращусь к обоим случаям. Вы могли бы хотеть к строкам дисплея:

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

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


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

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

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

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

  • \d средства любая цифра (это - короткий способ сказать [0-9] или [[:digit:]]).
  • x{4} соответствия x 4 раза. ({ } синтаксис не характерен для регулярных выражений Perl; это находится в расширенных регулярных выражениях через grep -E также.) Так \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 проверки на). Соответствия обычно выделяются в оттенке красного цвета (близко к вермильону), но я показал его в полужирном курсивном. Вот снимок экрана:
Screenshot showing that grep command, with 12345abc789d0123e4 as output, with the 0123 highlighted in red.

И можно даже сделать 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])).

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

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

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

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

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

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

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

Если Вы действительно предпочитаете a 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)* или ([0-9]{0,4}[^0-9])* нуль соответствий или больше (*) экземпляры не больше чем четырех цифр сопровождаются нецифрой. Нулевые цифры (т.е. ничто) являются одной возможностью для "не больше чем четырех цифр". Это соответствует (a) пустой строке или (b) любой строке, заканчивающейся в нецифре и не содержащей любые последовательности больше чем четырех цифр.

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

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

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

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

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

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

52
ответ дан 23 November 2019 в 00:33

Это покажет Вам 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]
9
ответ дан 23 November 2019 в 00:33

Если 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 повторяет паттерн.

0
ответ дан 23 November 2019 в 00:33

Можно попробовать ниже команды путем замены файла фактическое имя файла в системе, которую можно также проверить это учебное руководство на большее количество использования команды grep:

grep-E' (^ | [^0-9]) [0-9] {4} ($ | [^0-9])' файл

0
ответ дан 23 November 2019 в 00:33

Другие вопросы по тегам:

Похожие вопросы: