Есть два способа интерпретировать этот вопрос; Я рассмотрю оба случая. Возможно, вы захотите отобразить строки:
- которые содержат последовательность из четырех цифр, которая сама по себе не является частью более длинной последовательности цифр, или
- которая содержит последовательность из четырех цифр, но больше не является последовательностью цифр (даже отдельно).
Например, (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
И вы даже можете grep
напечатать только соответствующий текст, а не всю строку, с помощью -o
:
ek@Io:~$ grep -oP '(?<!\d)\d{4}(?!\d)' <<< 12345abc789d0123e4
0123
Альтернативный путь, без заглядывающих и заглядывающих утверждений
Однако, если вы:
- нужна команда, которая также будет работать в системах, где
grep
она не поддерживает -P
или иным образом не хочет использовать регулярное выражение Perl, и
- не нужно специально сопоставлять четыре цифры - что обычно бывает, если ваша цель просто отображать строки, содержащие совпадения, и
- все в порядке с решением, которое немного менее элегантно
... тогда вы можете достичь этого с помощью расширенного регулярного выражения :
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])
).
Если же , с другой стороны, вы хотите , чтобы отобразить все строки, содержащие последовательность из четырех цифр, но не содержат какой - либо последовательности более четырех цифр (даже тот , который отделен от другой последовательности только четыре цифры), то концептуально ваш Цель состоит в том, чтобы найти линии, которые соответствуют одному шаблону, но не другому.
Поэтому, даже если вы знаете , как сделать это с помощью одного шаблона, я предложил бы использовать что - то вроде Мэтта второго предложения, grep
ИНГ для двух моделей отдельно.
При этом вы не получаете никаких преимуществ от расширенных функций регулярных выражений 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
команду, которая
- использует одно регулярное выражение (не два
grep
s, разделенные каналом , как указано выше)
- отображать строки, содержащие как минимум одну последовательность из четырех цифр,
- но нет последовательности из пяти (или более) цифр,
- и вы не против сопоставить всю строку, а не только цифры (вы, вероятно, не против этого)
... тогда вы можете использовать:
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])*
соответствует нулю или более ( *
) экземпляров не более четырех цифр, за которыми следует не цифра. Нулевые цифры (т. Е. Ничего) - это одна возможность для «не более четырех цифр». Это соответствует (а) пустой строке или (б) любой строке, заканчивающейся нецифровой цифрой и не содержащей последовательности из более чем четырех цифр.
Поскольку текст, находящийся непосредственно слева от центральной части \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}'
Таким образом, ясно, что ваша цель состоит в том, чтобы выбрать строки, которые содержат одно, а не другое. Кроме того, синтаксис более прост (так что многие читатели / сопровождающие могут его быстрее понять).
1234a12345
ли отображаться строка как или нет?