Есть два способа интерпретировать этот вопрос; я рассмотрю оба случая. Возможно, вы захотите отобразить строки:
- которые содержат последовательность из четырех цифр, которая сама по себе не является частью какой-либо более длинной последовательности цифр, или
- который содержит последовательность из четырех цифр, но больше не последовательность цифр (даже по отдельности).
Например, (1) будет отображать 1234a56789
, но (2) не стал бы этого делать.
Если вы хотите отобразить все строки, содержащие последовательность из четырех цифр, которая сама по себе не является частью какой-либо более длинной последовательности цифр, одним из способов является:
grep -P '(?<!\d)\d{4}(?!\d)' файл
Это использует Регулярные выражения 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)' <<< 12345abc789d0123e412345abc789d0123e4
По умолчанию в Ubuntu каждый пользователь имеет alias grep='grep --color=auto'
в их ~.bashrc
файл. Таким образом, вы автоматически получаете цветовую подсветку при выполнении простой команды, начинающейся с grep
(это когда псевдонимы расширяются) и стандартный выходной сигнал является терминал (это то, что --color=авто
проверяет наличие). Совпадения обычно выделяются красным цветом (близким к алый), но я выделил его жирным курсивом. Вот скриншот:
И вы даже можете сделать grep
выводите только соответствующий текст, а не всю строку целиком, с -o
:
ek@Io:~$ grep -oP '(?<!\d)\d{4}(?!\d)' <<< 12345abc789d0123e40123
Альтернативный Способ, Без Оглядывающиеся назад и заглядывающие вперед утверждения
Однако, если вы:
- нужна команда, которая также будет выполняться в системах, где
grep
не поддерживает-P
или иным образом не хотите использовать регулярное выражение Perl, и - не нужно специально сопоставлять четыре цифры, что обычно имеет место, если ваша цель - просто отобразить строки, содержащие совпадения, и
- вас устраивает решение, которое немного менее элегантно
... тогда вы можете достичь этого с помощью расширенное регулярное выражение вместо:
grep -E '(^|[^0-9])[0-9]{4}($|[^0-9])' файл
Это соответствует четырем цифрам и окружающему их нецифровому символу - или началу или концу строки. Конкретно:
-
[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])
).
Если, с другой стороны, вы хотите отобразить все строки, содержащие четырехзначную последовательность, но не содержащие какой-нибудь последовательность из более чем четырех цифр (даже та, которая отделена от другой последовательности всего из четырех цифр), тогда концептуально ваша цель - найти строки, соответствующие одному шаблону, но не другому.
Поэтому, даже если вы знаете, как это сделать с помощью одного шаблона, я бы предложил использовать что-то вроде мэтта второе предложение, grep
поиск по двум шаблонам отдельно.
При этом вы не получите большой пользы от каких-либо дополнительных функций регулярных выражений Perl, поэтому вы можете предпочесть не использовать их. Но в соответствии с приведенным выше стилем, вот сокращение решение Мэтта с помощью \d
(и фигурные скобки) вместо [0-9]
:
grep -P '\d{4}' файл | grep -Pv '\d{5}'
Поскольку он использует [0-9]
, путь Мэтта является более портативным - он будет работать в системах, где grep
не поддерживает регулярные выражения Perl. Если вы используете [0-9]
(или [[:digit:]]
) вместо \d
, но продолжайте использовать {
}
, вы получаете переносимость пути Мэтта немного более кратко:
grep -E '[0-9]{4}' файл | grep -Ev '[0-9]{5}'
Альтернативный Способ, С Одним Шаблоном
Если вы действительно предпочитаете grep
приказать, чтобы
-
использует одно регулярное выражение (не два
grep
s, разделенные символом труба, как указано выше) - для отображения строк, содержащих по крайней мере одну последовательность из четырех цифр,
- но никаких последовательностей из пяти (или более) цифр,
- и вы не возражаете против сопоставления всей строки, а не только цифр (вы, вероятно, не возражаете против этого)
... затем вы можете использовать:
grep -Px '(\d{0,4}\D)*\d{4}(\D\d{0,4})*' файл
То -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})*' файл
Способ работы этих регулярных выражений заключается в следующем:
Посередине,
\d{4}
или[0-9]{4}
соответствует одной последовательности из четырех цифр. У нас может быть больше одного из них, но нам нужно иметь хотя бы один.-
Слева,
(\d{0,4}\D)*
или([0-9]{0,4}[^0-9])*
соответствует нулю или более (*
) экземпляры, содержащие не более четырех цифр, за которыми следует не-цифра. Нулевые цифры (т.е. ничего) - это одна из возможностей для "не более четырех цифр". Это соответствует (а) пустая строка или (b) любая строка окончание в нецифровом формате и не содержащем никаких последовательностей из более чем четырех цифр.Поскольку текст сразу слева от центрального
\d{4}
(или[0-9]{4}
) должно быть либо пустым, либо заканчиваться не цифрой, это предотвращает центральный\d{4}
из совпадающих четырех цифр, у которых есть другая (пятая) цифра слева от них. -
Справа,
(\D\d{0,4})*
или([^0-9][0-9]{0,4})*
соответствует нулю или более (*
) экземпляры не-цифры, за которыми следует не более четырех цифр (которые, как и раньше, могут быть четырьмя, тремя, двумя, одной или даже вообще отсутствовать). Это соответствует (а) пустая строка или (b) любая строка начало в нецифровом формате и не содержащем никаких последовательностей из более чем четырех цифр.Поскольку текст сразу справа от центрального
\d{4}
(или[0-9]{4}
) должно быть либо пустым, либо начинаться с не-цифры, это предотвращает центральный\d{4}
из совпадающих четырех цифр, у которых есть еще одна (пятая) цифра справа от них.
Это гарантирует, что где-то присутствует последовательность из четырех цифр и что нигде нет последовательности из пяти или более цифр.
Нет ничего плохого или неправильного в том, чтобы делать это таким образом. Но, возможно, самая важная причина рассмотреть эту альтернативу заключается в том, что она разъясняет преимущества использования grep -P '\d{4}' файл | grep -Pv '\d{5}'
(или аналогичный) вместо этого, как было предложено выше и в ответ Мэтта.
Таким образом, становится ясно, что ваша цель - выбрать строки, которые содержат одно, но не другое. Плюс синтаксис проще (так что он может быть быстрее понят многими читателями / сопровождающими).