У цьому розділі ви дізнаєтесь:

  • Про те, як використовувати регулярні вирази
  • Про метасимволи у регулярному виразі
  • Пошук по зразку у файлах та виводі команд
  • Діапазони символів та класи символів у Bash.

Що таке «регулярний вираз»?

«Регулярний вираз» -- це текстовий шаблон, що може співпадати з набором рядків. Він задає правила, згідно з якими рядок або співпадає з шаблоном, або не співпадає. Наведемо декілька словесних прикладів таких шаблонів: «три цифри» (з таким шаблоном співпадають рядки «121», 424», але не «23абв»), «дві цифри, після яких стоїть крапка, за якою йдуть декілька цифр, проте не менше однієї» (с цим шаблоном співпадають «11.2», «11.4а», але ж не «11. »). Регулярні вирази подібні арифметичним виразам у тому, що формуються з менших виразів за допомогою операторів

Найпростіший регулярний вираз, з яких формуються складні, є звичайним символом. Більшість символів, включаючи усі літери та цифри, є регулярними виразами, що співпадають із відповідними символами в рядках.

Кожний метасимвол з особливим значенням мусить екрануватись попереду символом зворотної похилої риски.

Метасимволи у регулярних виразах

Після регулярного виразу можна вказати один чи декілька операторів повтору (метасимволів):

[Оператори у регулярних виразах] | Оператор | Ефект | :------- | :--------------------------------------------------------------------- | . | Співпадає з будь-яким символом. | ? | Попередній вираз є необов'язковим і може збігатися максимум один раз. | * | Попередній вираз повинен співпадати нуль чи більше разів. | + | Попередній вираз повинен співпадати один чи більше разів. | {N} | Попередній вираз повинен співпадати рівно N разів. | {N,} | Попередній вираз повинен співпадати N чи більше разів. | {N,M} | Попередній вираз повинен співпадати не менш ніж N, однак не більше за М разів. | - | Якщо цей символ знаходиться не на початку й не в кінці списку чи інтервалу у списку, задає інтервал. | ^ | Співпадає з початком рядка, при цьому не потребує символу. Якщо стоїть на початку списку, інвертує його (збігається з символом, якого немає у списку) | $ | Співпадає з кінцем рядка. | \b | Співпадає з межею слова. | \B | Співпадає з порожнім рядком, якщо у цьому місці не закінчується або не починається слово. | \< | Співпадає з пустим рядком у початку слова. | \> | Співпадає з пустим рядком у кінці слова.

Два регулярних вирази можна об'єднати. У цьому разі отриманий складний вираз буде співпадати з рядком, що є об'єднанням двох рядків, які співпадають з наведеними регулярними виразами.

Два регулярних вирази можна з'єднати інфіксним оператором «|»; у цьому разі складний вираз буде співпадати з рядками, що співпадають або з першим, або з другим простим регулярним виразом.

Оператори повтору мають більший пріоритет, ніж оператор об'єднання, а останній у свою чергу має більший пріоритет, ніж оператор альтернативи. Ці правила пріоритету можна змінити за допомогою круглих дужок.

Стандартні та поширені регулярні вирази

У стандартних регулярних виразах метасимволи «?», «+», «{», «|», «(», та «)» втрачають своє спеціальне значення, замість цього слід вживати «\?», «+», «{», «|», «(», та «)».

Перевірте у документації до вашої системи, чи підтримують команди, що працюють з регулярними виразами, поширені вирази.

Приклади з grep

Що таке grep

grep шукає у файлах рядки, що містять співпадання з наданим набором шаблонів. Ці рядки виводяться на стандартний вивід, формат виводу можна задати за допомогою параметрів.

Незважаючи на те, що grep призначений для пошуку текстової інформації, рядки у файли можуть містити будь-які символи, та можуть бути будь-якої довжини. Якщо останній символ файлу не є символом нового рядка, grep все одно буде вважати такий останній рядок звичайним рядком з символом нового рядка на кінці. Оскільки символ нового рядка є також розділовим символом у списку шаблонів, немає ніякої можливості вказати цей символ безпосередньо у шаблоні.

Декілька прикладів:

cathy ~> grep root /etc/passwd
root:x:0:0:root:/root:/bin/bash
operator:x:11:0:operator:/root:/sbin/nologin

cathy ~> grep -n root /etc/passwd
1:root:x:0:0:root:/root:/bin/bash
12:operator:x:11:0:operator:/root:/sbin/nologin

cathy ~> grep -v bash /etc/passwd | grep -v nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
news:x:9:13:news:/var/spool/news:
mailnull:x:47:47::/var/spool/mqueue:/dev/null
xfs:x:43:43:X Font Server:/etc/X11/fs:/bin/false
rpc:x:32:32:Portmapper RPC user:/:/bin/false
nscd:x:28:28:NSCD Daemon:/:/bin/false
named:x:25:25:Named:/var/named:/bin/false
squid:x:23:23::/var/spool/squid:/dev/null
ldap:x:55:55:LDAP User:/var/lib/ldap:/bin/false
apache:x:48:48:Apache:/var/www:/bin/false

cathy ~> grep -c false /etc/passwd
7

cathy ~> grep -i ps ~/.bash* | grep -v history
/home/cathy/.bashrc:PS1="\[\033[1;44m\]$USER is in \w\[\033[0m\] "

У першій команді користувач cathy відображає рядки, що містять слово root, із файлу /etc/passwd.

Далі вона відображає ці ж самі рядки, але з їх порядковими номерами попереду.

Третя команда відображає користувачів, що не користуються bash, окрім тих, у яких оболонкою вказано nologin.

Далі відображається кількість користувачів, у яких оболонкою вказано /bin/false.

Остання команда виводить рядки з усіх файлів у домашньому каталозі, що починаються з символів ~/.bash, крім тих рядків, що містять слово history (наприклад, щоб пропустити рядки з історії команд).

Подивимось, що ще ми можемо зробити за допомогою grep та регулярних виразів.

Grep та регулярні вирази

Нотатка для користувачів відмінних від Linux операційних систем: всі приклади, що наведено далі, працюють у GNU grep, який підтримує розширені регулярні вирази. На системах з Linux стандартно встановлюється саме GNU grep. То ж якщо ви працюєте з комерційною системою, перевірте, який саме grep встановлено у систему; для цього запустіть цю програму з параметром -v. Ви можете завантажити GNU grep з Web-сайту http://gnu.org/directory/.

Границі рядків та слів

Змінимо попередній приклад таким чином, щоб відобразити лише ті рядки, що починаються з «root»:

cathy ~> grep ^root /etc/passwd
root:x:0:0:root:/root:/bin/bash

Якщо нам потрібно знайти користувачів, що не мають встановленої оболонки, ми мусимо шукати рядки, що закінчуються на «:»:

cathy ~> grep :$ /etc/passwd
news:x:9:13:news:/var/spool/news:

Щоб перевірити, чи було у файлі ~/.bashrc експортовано змінну PATH у середовище, спочатку треба знайти усі рядки, що містять слово «export», після чого з цих рядків відобразити ті, у яких є слова, що починаються з PATH; саме слова, бо ж нам не потрібні інші змінні, що містять літери PATH -- MANPATH, та таке інше.

cathy ~> grep export ~/.bashrc | grep '\<PATH'
  export PATH="/bin:/usr/lib/mh:/lib:/usr/bin:/usr/local/bin:/usr/ucb:/usr/dbin:$PATH"

Неважко здогадатися, що > співпадає на кінці слова.

Щоб просто знайти рядок-аргумент як слово -- тобто як символи, відокремлені від іншого тексту пробілами -- треба вказати параметр -w, як показано у наступному прикладі, де ми вилучаємо інформацію про кореневу файлову систему:

cathy ~> grep -w / /etc/fstab
LABEL=/                 /                       ext3    defaults 1 1

Якщо не вказати цього параметра, на екран буде виведено усі рядки з таблиці файлових систем.

Символьні класи

Вираз у кутових дужках -- це список символів, взятий у кутові дужки «[» та «]». Він співпадає з будь-яким символом зі списку; якщо перший символ у списку є символом «^», то вираз співпадає з будь-яким символом, якого НЕМАЄ у списку. Наприклад, регулярний вираз «[0123456789]» співпадає з будь-якою цифрою.

У списку можна вказати «інтервальний вираз» -- два символи, розділені одним знаком мінуса. Цей вираз співпадає з будь-яким символом, що за алфавітом знаходиться між вказаних двух символів включно, згідно з системною локаллю та символьним набором. Наприклад, у локалі C вираз «[a-d]» відповідає виразу «[abcd]». Деякі локалі розміщують символи у словниковий лад, то ж в них «[a-d]» зазвичай не є еквівалентом «[abcd]»; наприклад, цей вираз може відповідати «[aAbBcCdD]». Щоб повернути звичайну інтерпретацію інтервалів, можна встановити для процесу grep локаль C; для цього потрібно призначити змінної LC_ALL значення «C».

Треба зауважити, що є декілька названих символьних класів, які можна вказувати у виразах у кутових дужках. Дивіться man- та info-сторінки стосовно цих визначених класів.

cathy ~> grep [yf] /etc/group
sys:x:3:root,bin,adm
tty:x:5:
mail:x:12:mail,postfix
ftp:x:50:
nobody:x:99:
floppy:x:19:
xfs:x:43:
nfsnobody:x:65534:
postfix:x:89:

cathy ~> ls *[1-9].xml
app1.xml  chap1.xml  chap2.xml  chap3.xml  chap4.xml

У цьому прикладі спершу вилучають усі рядки, що містять один з символів «y» та «f», а потім назви усіх файлів, які закінчуються будь-якою цифрою та розширенням .xml.

Байдужий символ

Символ «.» у регулярному виразі співпадає з одним будь-яким символом,крiм символу нового рядка. Наприклад, якщо ви хочете знайти усі англійські п'ятилітерні слова, що починаються з «c» та закінчуються на «h» (зручно при розв'язуванні кросвордів), можна використати наступний регулярний вираз:

cathy ~> grep '\<c...h\>' /usr/share/dict/words
catch
clash
cloth
coach
couch
cough
crash
crush

Якщо вам треба відобразити усі рядки, що містять крапку, вкажіть параметр -F до grep.

Декілька символів можна знайти за допомогою метасимвола «*», як це показано у наступному прикладі:

cathy ~> grep '\<c.*h\>' /usr/share/dict/words
caliph
cash
catch
cheesecloth
cheetah
--output omitted--

Якщо вам треба знайти символ зірочки у файлі чи виводі команди, вкажіть параметр -F:

cathy ~> grep * /etc/profile

cathy ~> grep -F '*' /etc/profile
for i in /etc/profile.d/*.sh ; do

Порівняння з шаблоном у Bash

Діапазони символів

Bash має дуже зручну можливість -- зіставляння назв із шаблоном, то ж вам немає сенсу у цьому разі використовувати grep чи будь-яку іншу команду.

Ви вже знаєте, що символ зірочки (*) збігається з будь-якою кількістю символів, а знак питання (?) -- з одним символом. Щоб використати ці метасимволи буквально, у будь-який метод заекрануйте їх:

cathy ~> touch "*"

cathy ~> ls "*"
*

Також можна вказати, які саме символи будуть збігатися -- для цього використовують діапазони символів. Щоб вказати діапазон, у квадратових дужках перелічить усі потрібні символи чи інтервали символів, якщо між двома символами стоїть риска. Наприклад:

cathy ~> ls -ld [a-cx-z]*
drwxr-xr-x    2 cathy    cathy      4096 Jul 20  2002 app-defaults/
drwxrwxr-x    4 cathy    cathy          4096 May 25  2002 arabic/
drwxrwxr-x    2 cathy    cathy          4096 Mar  4 18:30 bin/
drwxr-xr-x    7 cathy    cathy          4096 Sep  2  2001 crossover/
drwxrwxr-x    3 cathy    cathy          4096 Mar 22  2002 xml/

Ця команда показує усі файли з домашнього каталогу користувача cathy, що починаються з «a», «b», «c», «x», «y» чи «z».

Якщо першим символом після відкриваючої квадратової дужки є «!» чи «^», то співпадає будь-який символ, що не міститься у діапазоні. Щоб добавити символ риски у діапазон, вкажіть його першим чи останнім символом діапазону. Порядок символів у інтервалі залежить від локалі та значення змінної LC_COLLATE, якщо її було встановлено. Наприклад, деякі локалі можуть тлумачити «[a-cx-z]» як «[aBbCcXxYyZz]», якщо встановлено словниковий порядок символів. Щоб бути впевненим у традиційному поводженні діапазонів, встановіть LC_COLLATE чи LC_ALL у значення «C».

Символьні класи

Символьні класи можна вказати у діапазоні як [:CLASS:], де CLASS може мати одно із вказаних стандартом POSIX значень:

«alnum», «alpha», «ascii», «blank», «cntrl», «digit», «graph», «lower», «print», «punct», «space», «upper», «word» чи «xdigit».

Декілька прикладів:

cathy ~> ls -ld <span class="createlink"><a href="/cgi-bin/ikiwiki.cgi?page=%3Adigit%3A&amp;from=LDP%2FBash_beginners_guide%2Fregular_expressions&amp;do=create" rel="nofollow">?</a>:digit:</span>*
drwxrwxr-x    2 cathy   cathy       4096 Apr 20 13:45 2/

cathy ~> ls -ld <span class="createlink"><a href="/cgi-bin/ikiwiki.cgi?page=%3Aupper%3A&amp;from=LDP%2FBash_beginners_guide%2Fregular_expressions&amp;do=create" rel="nofollow">?</a>:upper:</span>*
drwxrwxr--    3 cathy   cathy           4096 Sep 30  2001 Nautilus/
drwxrwxr-x    4 cathy   cathy           4096 Jul 11  2002 OpenOffice.org1.0/
-rw-rw-r--    1 cathy   cathy         997376 Apr 18 15:39 Schedule.sdc

Якщо включено опцію оболонки extglob (за допомогою внутрішньої команди shopt), доступні поширені можливості зіставляння з шаблоном. Більш докладну інформацію можна знайти на Info-сторінці Bash у розділі «Basic shell features->Shell Expansions->Filename Expansion->Pattern Matching».

Підсумок

Регулярні вирази є дуже зручним інструментом вилучення потрібних рядків із файлів та виводу команд. Вони використовуються багатьма програмами UNIX: vim, perl, СКБД PostgreSQL та іншими. Їх можна використовувати у будь-якій мові програмування за допомогою зовнішніх бібліотек, більш того, їх можна знайти навіть поза межами світу UNIX. Наприклад, вони використовуються в електронній таблиці Excel, що входить до пакету Microsoft Office. У цьому розділі ми практикувалися у використанні команди grep, що є обов'язковою частиною будь-якого середовища UNIX.

Зауважте, будь-ласка, що у цьому розділі ми тільки-но ознайомилися з можливостями grep, використовуючи його як приклад вживання регулярних виразів. GNU grep постачається з вичерпною документацією, яку ви просто повинні прочитати!

Bash має вбудовані можливості зіставляння з шаблонами та підтримує символьні діапазони та символьні класи.

Вправи

Ці вправи допоможуть вам оволодіти мистецтвом вживання регулярних виразів:

  1. Покажіть на екрані список усіх користувачів системи, що мають Bash звичайною оболонкою.
  2. Покажіть усі рядки з файлу /etc/group, що починаються послідовністю символів «daemon».
  3. Покажіть усі рядки з того ж файлу, що не містять її.
  4. Відобразите інформацію о комп'ютері localhost із файлу /etc/hosts, покажіть номер(и) рядків, що збіглися, та порахуйте кількість збігів.
  5. Покажіть список підкаталогів каталогу /usr/share/doc, що містять інформацію про оболонки.
  6. Яку кількість файлів README містять ці підкаталоги, не враховуючи файли вигляду «README.a_string»?
  7. За допомогою grep покажіть список файлів (не враховуючи каталоги) у вашому домашньому каталозі, що змінювалися менше, ніж 10 годин тому?
  8. Помістіть усі ці команди у файл сценарію, що буде видавати повний вивід.
  9. Чи ви зможете реалізувати wc -l лише за допомогою grep?
  10. За допомогою файлу /etc/fstab покажіть список локальних розділів.
  11. Напишіть сценарій, що перевіряє, чи міститься рахунок користувача у файлі /etc/passwd. Ім'я користувача вкажіть у сценарії, не треба обробляти аргументи командного рядка чи використовувати умовні оператори.
  12. Покажіть файли налаштувань у каталозі /etc, що містять цифри у своїх назвах.