Існує два способи тлумачення цього питання; Я торкнуся обох випадків. Можливо, ви захочете відобразити рядки:
- які містять послідовність з чотирьох цифр, яка сама по собі не є частиною жодної довшої послідовності цифр, або
- що містить чотиризначну послідовність, але більше не послідовність цифр (навіть не окремо).
Наприклад, (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]
, спосіб matt є більш портативним - він буде працювати в системах, де 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])*
збігається з нуля або більше ( *
) екземплярів не більше чотирьох цифр, за якими слід нецифровий. Нульові цифри (тобто нічого) - це одна можливість "не більше чотирьох цифр". Це відповідає (а) порожній рядку або (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}
зіставити чотири цифри, які мають іншу (п'яту) цифру праворуч від них.
Це забезпечує, що десь присутня чотиризначна послідовність, і ніде не існує послідовності з п'яти чи більше цифр.
Непогано чи неправильно це робити так. Але, мабуть, найважливіша причина розглянути цю альтернативу полягає в тому, що вона роз'яснює перевагу використання (або подібного) натомість, як було запропоновано вище та у відповіді matt .grep -P '\d{4}' file | grep -Pv '\d{5}'
Таким чином, зрозуміло, що ваша мета - вибрати рядки, які містять одне, а не інше. Плюс синтаксис - простіший (тому його можуть зрозуміти швидше багато читачів / підтримуючих).
1234a12345
, чи ні?