Існує два способи тлумачення цього питання; Я торкнуся обох випадків. Можливо, ви захочете відобразити рядки:
- які містять послідовність з чотирьох цифр, яка сама по собі не є частиною жодної довшої послідовності цифр, або
- що містить чотиризначну послідовність, але більше не послідовність цифр (навіть не окремо).
Наприклад, (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}відповідає x4 рази. ( { }синтаксис не характерний для регулярних виразів 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команді, що
- використовує єдиний регулярний вираз (не два
greps, розділені трубою , як вище)
- відображати рядки, що містять щонайменше одну послідовність із чотирьох цифр,
- але немає послідовностей з п'яти (або більше) цифр,
- і ви не проти зрівняти весь рядок, а не лише цифри (ви, мабуть, не проти цього)
... тоді ви можете використовувати:
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, чи ні?