Символьные шаблоны

Это способы найти в чём-то схожие фрагменты текста или кратко выразить длинный текст. Потом текст как-то обрабатывается (возможно, по другому шаблону) либо хотя бы выводится (на экран или в файл). Есть сколько-то диалектов шаблонов. Популярны три: "раскрытие имён в командной оболочке" (filename expansion, или globbing), "регулярные выражения POSIX" и "регулярные выражения, аналогичные применяемым в языке Perl" (Perl-compatible regular expressions, PCRE). Последние здесь не описаны.


Любой фрагмент текста является шаблоном (pattern) для самого себя. В пейджерах и редакторах есть функциональность поиска по фрагменту как он есть и, возможно, по шаблону на каком-либо диалекте. Для автоматизации применяют отдельные программы поиска по тексту: grep и более сложные awk, sed. Для тренировки можно, например, сделать текстовый вариант какой-либо man-страницы:


$ env COLUMNS=60 man bash | col -b > bash.txt


$ grep -i "pattern matching" < bash.txt

below under Pattern Matching, as if the extglob

sion and pattern matching.

matching the pattern (see Pattern Matching below).

Pattern Matching

performs pattern matching as described above

pattern matching bracket expressions

(see Pattern Matching above) behave


Обычно программа grep выводит всю строку с совпавшим фрагментом. Перенаправление ввода для "bash.txt" здесь просто для наглядности. Опция -i означает "игнорировать регистр", то есть, например, символы p и P считаются одинаковыми. Но коды строчных и заглавных букв отличаются. Для каждого алфавита программам нужны свои правила распознавания букв.


Параграф под заголовком "Pattern Matching" в man-странице Bash описывает как раз шаблоны раскрытия имён. Найдено семь строк, а нужна одна (четвёртая). Она отличается, например, тем, что после Matching строка заканчивается. В регулярных выражениях конец строки помечают знаком $; начало - ^.


$ grep -i -A 8 "pattern matching$" < bash.txt

Pattern Matching


Any character that appears in a pattern, other than

the special pattern characters described below,

matches itself. The NUL character may not occur in

a pattern. A backslash escapes the following char‐

acter; the escaping backslash is discarded when

matching. The special pattern characters must be

quoted if they are to be matched literally.


Теперь точно найдена одна строка; остальные - результат опции "-A 8", то есть "восемь линий после найденной". Это бывает удобно для уточнения (действительно ли найдено нужное) или для составления краткой сводки. Документация: страницы man и info для grep.


Программа find находит файлы по метаданным; результат можно перенаправить в grep, чтобы дальше искать уже по текстовому содержимому. Пример (вывод не показан):


$ find . -type f -iname "*.txt" -print0 \

| xargs -0 grep -i "09.2017"


Здесь find находит файлы с именем, заканчивающимся на ".txt". Символ * означает "любое (в том числе нисколько) количество любых символов". Опция -0 - для приёма нуль-терминальных строк от опции -print0. Вся команда должна в итоге выдать записи за сентябрь 2017 года, обнаруженные (если есть) в содержимом (не в метаданных) найденных текстовых файлов.


Сама grep тоже может выполнить поиск файлов - обработать полное содержимое рабочего каталога с опцией -R. Но тогда ей придётся прочитать содержимое всех файлов. С find будет быстрее, если примерно известно имя файла.


Поиск файла и составление шаблона схожи: первичный поиск может дать слишком много результатов, и тогда вы придумываете, как надо исправить условия поиска; первый составленный шаблон может не подойти, и тогда вы уточняете шаблон.


Шаблон может включать в себя фрагменты имён и метасимволы вперемешку. Символы *, ? и некоторые другие в шаблонах называют "метасИмволами" (metacharacters), или "джОкерными символами" (wildcard characters), - в том смысле, что они представляют не самих себя. Чтобы метасимвол (или нетипичный символ) был воспринят как есть, его нужно экранировать. Или заключить в кавычки: шаблон внутри кавычек не раскрывается оболочкой. Прочие символы (буквы, цифры...) воспринимаются как есть.


Например, вы смутно помните, что где-то был файл с именем, начинающимся на par. Тогда шаблон для find или оболочки будет выглядеть так: "par*". Или приведённый выше шаблон "*.txt" - для всех имён файлов, заканчивающихся на ".txt".


Когда оболочка разбирает команду, то раскрывает шаблоны (globbing), то есть заменяет их на подходящие имена. Вместо шаблона программа в команде получает от оболочки подходящий список имён файлов (если есть) из рабочего или заданного каталога (не вложенных). Можно потренироваться с программой echo (или ls):


# Создать тестовые файлы и каталоги.

$ mkdir testdir

$ cd !^

$ mkdir .hd1 .hd2

$ touch .hf1 .hf2 \

a.txt b.txt \

f1 f2 f3 f4 f5 f6 f7 f8 f9 f10


# Символ * означает "любое (в том числе нисколько)

# количество любых символов". (Есть исключение.)

$ echo f*

f1 f10 f2 f3 f4 f5 f6 f7 f8 f9

$ echo *

a.txt b.txt f1 f10 f2 f3 f4 f5 f6 f7 f8 f9

$ echo *.txt

a.txt b.txt

# Символ ? означает "один любой символ".

$ echo ???

f10

# Если нечем раскрывать шаблон, оболочка передаёт его

# программе как есть.

$ echo ????

????

# "Имена длиной не менее трёх символов".

$ echo ???*

a.txt b.txt f10

# Квадратные скобки в шаблонах содержат возможные варианты

# одиночных символов.

$ echo f[1-4]

f1 f2 f3 f4

$ echo f[579]

f5 f7 f9

$ echo [ab].???

a.txt b.txt

# Символ ^ или ! означает отрицание/несовпадение.

$ echo .h[^f]*

.hd1 .hd2

$ echo f[!1-4]

f5 f6 f7 f8 f9

# Бывает, что ввести список вручную или из файла

# оказывается проще, чем составить шаблон.

$ echo .h[df][^2]

.hd1 .hf1


Шаблон "*" (без кавычек) не раскрывается в имена скрытых файлов (вида ".имя"). Здесь шаблон ".*" тоже не годится, потому что раскрывается в том числе в имена-ссылки "." и "..". Значит, верный шаблон должен соответствовать такому набору символов: точка, затем не точка, затем любые другие символы либо ничего. Итого:


$ echo .[!.]*

.hd1 .hd2 .hf1 .hf2


Шаблонов, как любых аргументов, может быть несколько, поэтому для всех (не вложенных) файлов каталога нужно два шаблона (вывод не показан):


$ echo .[!.]* *


Первоначально [под]множества символов были диапазонами ASCII-символов: A-Z, a-z, 0-9 - основанными на диапазонах кодов. Для кириллицы и других алфавитов диапазоны в шаблоне могут не сработать.


Вышеописанные шаблоны обычно применяют в массовых файловых операциях: копировании, перемещении, удалении и иных. Пример:


$ mkdir ~/testdir2

$ cp ~/testdir1/f[1-9]* ~/testdir2/


Традиционное предупреждение: постарайтесь не сделать вот такую опечатку (лишний пробел между 2 и /), особенно работая от имени суперпользователя:


$ rm -rf ~/testdir2 /*


Потому что шаблон "/*" будет раскрыт в содержимое корневого каталога. Операция удаления (прервать - как обычно Ctrl C) может уничтожить и операционную систему, и ценные данные.

Диапазон или перечисление в Bash также можно задать фигурными скобками:


$ echo f{1..10}

f1 f2 f3 f4 f5 f6 f7 f8 f9 f10

$ echo f{,5,7,9}

f f5 f7 f9

$ echo {a..c}{1,2,3}

a1 a2 a3 b1 b2 b3 c1 c2 c3


Фигурный шаблон раскрывается независимо от наличия файлов с подходящими именами. Работают только числа и латиница. Скобки диапазонов могут быть вложенными. Фигурные шаблоны обычно применяют как генераторы имён. Пример:


$ mkdir -p ~/testdir1/2018/{01..12}


Шаблон внутри кавычек не раскрывается. Есть разница в обработке кавычек оболочкой. Обратные (`) применяют для подстановки команд; равноценный синтаксис - $(). Одиночные (') и двойные (") - для указания/уточнения границ аргумента.


Содержимое внутри одиночных кавычек никак предварительно не обрабатывается, а передаётся программе как есть.


Содержимое внутри двойных кавычек частично обрабатывается: оболочка подставляет значения переменных вместо их имён.


Выражения в кавычках могут быть вложенными; желательно чередовать виды кавычек. Кавычки должны быть парными (чётное число) либо экранированными. Парой считается ближайшая одинаковая кавычка. Примеры:


$ echo $SHELL '$SHELL' "$SHELL"

/bin/bash $SHELL /bin/bash

$ echo 'What's the shell file? It's "$SHELL"'

Whats the shell file? Its "$SHELL"

$ echo "What's the shell file? It's \"$SHELL\""

What's the shell file? It's "/bin/bash"


Некоторая функциональность может не работать, когда Bash запущена под именем sh или с опцией --posix (то есть имитирует некоторые старые версии). Чтобы лучше понимать разбор команд, можно запустить Bash в "отладочном" (debugging) режиме: bash -x.


Специализированные шаблоны для вывода информации есть, например, у программы date и у множества реализаций функции printf.


Например, у date шаблон %d означает текущий день, %m - месяц, %Y - год, %H - час (0-23), %M - минуту (с ведущими нулями, где нужно):


$ date -d '7:05 8/5' '+%d.%m.%Y %H:%M'

05.08.2017 07:05


Реализация printf есть, например, у find или stat. Есть шаблоны как у программы date (d, m, Y в сочетании с T, что означает "дата/время последнего изменения") и другие (%U - UID, %G - GID и прочие):


$ find /sbin -maxdepth 1 \

-printf '%Y %m%5U%5G %11s %Td.%Tm.%Ty-%TH:%TM %f\n' \

| head -5

d 755 0 0  12288 16.11.17-08:23 sbin

f 777 0 0  3 16.04.16-11:58 pvs

f 777 0 0  15 28.01.17-20:54 mount.lowntfs-3g

f 755 0 0  10552 18.11.14-18:57 crda

f 755 0 0  14410 16.04.16-11:57 fsadm


Документация: страницы man и info для date, find, printf(1), stat(1).


Есть сколько-то диалектов регулярных выражений, отчасти схожих; единого фактического стандарта нет. Разные программы могут воспринимать по-разному один и тот же шаблон. Любой шаблон следует тщательно тестировать на разных примерах в каждой применяемой программе.


Английские тексты относительно легко обрабатывать по шаблонам, потому что форм слов немного. Русские тексты обрабатывать труднее или даже невозможно.


Регулярные выражения состоят из условных элементов: собственно символов и их диапазонов/классов, якорей, метасимволов, квантификаторов и других.


Скобки [] здесь - обрамляют один символ из указанных диапазонов. Например, слова grey и gray оба правильны, поэтому их общий шаблон можно представить как "gr[ea]y". Далее, в начале предложения слова пишут с заглавной буквы, поэтому - "[Gg]r[ea]y".


$ echo -e "gray\nWhite\nGrey\ngreen"

gray

White

Grey

Green

$ echo -e "gray\nWhite\nGrey\ngreen" | grep '[Gg]r[ea]y'

gray

Grey


Чтобы не перечислять все возможные символы диапазона, можно указать границы через дефис; не буквы и не цифры (в том числе скобки) следует экранировать. Можно указать несколько диапазонов, например шаблон "[A-Za-z]" означает "один из символов латиницы, заглавных и строчных".


$ echo -e '-\na\nZ\n1\n&\n?\n[' | grep '[a-zA-Z\[\?\-]'

-

a

Z

?

[


Метасимволы: . - один любой символ; ^ - отрицание/исключение, то есть "любой символ, не входящий в указанные диапазоны" (его указывать первым, сразу после [).


$ echo -e '-\na\nZ\n1\n&\n?\n[' | grep '[^a-zA-Z\[\?\-]'

1

&

$ echo -e "four\nfork\nforty" | grep 'fo...'

forty


"Якоря" (anchors) здесь - указания на границы текстовых объектов: ^ - в начале строки; $ - в конце строки; \b - на границе слова; \B - не на границе слова; \< - в начале слова; \> - в конце слова.


$ echo -e "four\nfork\nforty" | grep 'fo..$'

four

fork

$ echo -e "before\nfour\nfork\nforty" | grep '\<for'

fork

forty


Шаблон "^$" будет означать пустую строку. Опция -c для grep означает "подсчитать найденные совпадения", например:


$ grep -c '^' < bash.txt

7579

$ grep -c '^$' < bash.txt

549


Итого 549 пустых строк из общего числа в 7579.


Иногда можно частично обойтись без шаблонов при помощи опций. Например, -v означает отрицание/исключение/несовпадение:


$ grep -v -c '^$' < bash.txt

7030


То есть (7579 - 549) = 7030 непустых строк.


"КвантификАторы" (quantifiers) здесь - повторители для предыдущих символов: ? - один/ни одного символа; * - ни одного/сколько угодно символов; + - один/сколько угодно символов; {N} - ровно N раз; {N, M} - от N до M раз включительно. Не применимы к якорям. Применимы к диапазонам (набирают после замыкающей скобки).


$ echo -e "colour\ncolor" | grep 'colo.*r'

colour

color


Шаблон 'colo.?r', вроде бы столь же пригодный для предыдущего сравнения, работает здесь только с опцией -E:


$ echo -e "colour\ncolor" | grep -E 'colo.?r'

colour

color


Круглые скобки применяют в сомнительных случаях для указания приоритета операций, подобно математическим выражениям. Нередко со скобками применяют метасимвол | в смысле союза "или":


$ echo -e "colour\ncolor" | grep -E '(colour|color)'

colour

color

$ echo -e "four\nfork\nforty" | grep -E 'fo(ur|rk)'

four

fork


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


Документация по регулярным выражениям в man-страницах (не очень понятна): grep, regex(7), pcresyntax(3), pcrepattern(3). В Интернете: https://en.wikipedia.org/wiki/Regular_expression.

GNU/Linux

1K постов15.5K подписчик

Добавить пост

Правила сообщества

Все дистрибутивы хороши.

Будьте людьми.