Данные, которые генерируют, обрабатывают и потребляют внешние и встроенные команды и конструкции интерпретатора, представляют собой текстовые потоки с произвольной структурой. Чаще всего в потоке можно выделить строки, отделяемые друг от друга управляющим символом перевода строки \n (CR, ^J с кодом 0х0А). Иногда в каждой строке выделяют поля, отделяемые друг от друга пробельными символами — управляющим символом горизонтальной табуляции \t (HT, ^| с кодом 0x09) или символом пробела (SPC с кодом 0x20) либо каким-то другим символом, зачастую символом двоеточия : или символом вертикальной черты |.
Для манипуляции текстовыми данными используют W:[регулярные выражения] — формальный язык поиска подстрок, удовлетворяющих определенным правилам (таблица ниже).
Регулярные выражения подобны шаблонным выражениям, которые применяются в подстановках имен файлов и в команде find при пойске файлов по критерию имени (-name).
Содержимое
Базовые регулярные выражения
Метасимвол | Значение |
• | Любой одиночный символ |
с* | Любое количество символов c |
•* | Любое количество любых символов |
[ab…z] | Любой символ из набора a, b,…, z |
[^ab…z] | Любой символ НЕ из набора a, b,…, z |
^ | Начало строки |
$ | Конец строки |
Регулярные выражения (RE, Regular Expressions) учитывают строковую структуру обрабатываемых данных и по сравнению с шаблонными выражениями вводят два дополнительных метасимвола — А и $, обозначающих начало и конец строки соответственно. Различают традиционные для UNIX базовые (BRE, Basic RE), расширенные (ERE, Extended RE) и Perl-совместимые регулярные выражения (PCRE, Perl Compatible RE).
Регулярные выражения в сравнении с шаблонными выражениями
Шаблонные выражения |
Регулярные выражения |
Значение |
? | • | Любой одиночный символ |
* | •* | Любое количество любых символов |
Основной набор инструментальных средств, используемых для обработки текстовых потоков, включает в себя фильтр строк grep, транслитератор tr, фильтр символов и полей cut, склейщик строк и полей paste, процессор таблиц awk и потоковый редактор sed.
Фильтр строк grep
Имя команды grep восходит к команде g древнейшего текстового редактора ed, которая использовала регулярные выражения re для глобального поиска строк и применения других команд редактора к ним, например команды печати p. В целом, встроенная команда редактора ed записывалась как g/re/p, что затем и дало название эквивалентной внешней команде grep, фильтрующей (и печатающей на поток вывода) строки, соответствующие заданному регулярному выражению.
В примерах из листингов ниже fgrep и grep используются для фильтрации строк, содержащих (-F) фиксированные, слова, что быстрее, чем обработка полных регулярных выражений.
Выборка строк: список сценариев интерпретатора
bender@ubuntu:~ $ file -Li /*bin/* /usr/*bin/* | fgrep shellscript
/bin/bzctnp: text/x-shellscript; charset=us-ascii
/bin/setupcon: text/x-shellscrlpt; charset*utf-8
/usr/sbin/upgrade-from-grub-legacy: text/x-shellscrlpt; charset=us-ascii
Выборка строк: RSS-память процессов браузера chromium
bender@ubuntu:~$ ps axo rss,comm | grep -F chromium
296628 chromium-browse
28432 chromium-browse
7444 chromium-browse
166672 chromium-browse
57948 chromium-browse
6466 chromium-browse
196752 chromium-browse
84872 chromium-browse
68628 chromium-browse
В примере из листинга ниже отфильтровываются строки конфигурационного файла команды wget, закомментированные символом # в их начале. Для этого выбираются строки, соответствующие регулярному выражению ^[^#], которое требует в начале строки ^ наличия символа, не ^ входящего в набор [#].
Выборка строк: фильтрация комментариев
bender@ubuntu:/etc$ wc -l /etc/wgetrc
126 /etc/wgetrc
bender@ubuntu:/etc$ grep ‘^[^#]’ /etc/wgetrc
passive_ftp = on
В листинге ниже при помощи регулярного выражения [^0-9][0-9][0-9][^0-9] выбираются строки с двузначными числами — содержащие два символа подряд из набора [0-9] (цифры), непосредственно перед которыми и после которых находятся символы не ^ из набора [0-9] (не цифры).
Выборка строк с двузначными числами
bender@ubuntu:/etc$ grep [^0-9][0-9][0-9][^0-9] /etc/services
systat 11/tcp users
daytime 13/tcp
Z3950 210/tcp wais # NISO Z39.50 database
#> Ports are used in the TCP [45,106] to name the ends of logical
rtcm-sc104 2101/tcp # RTCM SC-104 IANA 1/29/99
x11 6000/tcp x11-0 # X Window System
# The following is probably Kerberos v5 — [email protected] (11/02/2000)
linuxconf 98/tcp # LinuxConf
Фильтр символов и полей cut
Команда cut применяется для «вырезания» указанных (порядковым номером) символов или полей (по заданному разделителю) каждой строки. Несмотря на название команды, с содержимым файла никаких действий не производится, а символы и поля «выделяются» на поток вывода, что позволяет считать команду фильтром полей, аналогичным фильтру строк grep.
В примере из листинга ниже при помощи cut отфильтровывается первое поле (-f 1 -d :) тех строк классификатора файлов file, которые были предварительно отфильтрованы по наличию слова shellscript. В результате на поток stdout будут выведены только имена файлов, классифицированных как сценарии командного интерпретатора.
Выборка строк и полей с помощью grep и cut: список имен сценариев интерпретатора
bender@ubuntu:~ $ file -Li /*bin/* /usr/*bin/* | grep shellscript | cut -f 1 -d :
/bin/bzcmp
/bin/setupcon
/usr/sbin/upgrade-from-grub-legacy
Аналогично, в примере из листинга ниже из вывода команды ps отбираются строки свойств процессов с именем chromium, затем выделяются символы с 10-го по 80-й каждой строки (содержащие суммарное количество резидентной памяти процесса rss, помещенное в первые :8 символов).
Отобранные статистические данные склеиваются командой paste в одну строку (-s) посредством символа + в качестве разделителя (-d) и полученное таким образом арифметическое выражение отправляется на калькулятор bc для подсчета. В результате будет получено суммарное потребление физической памяти всеми процессами Web-браузера chromium.
Выборка столбцов и склейка полей: суммарная RSS-память процессов браузера chromium
bender@ubuntu:~$ ps axo rss:8,com | grep chromium | cut -c 1-8 | paste -s -d + | bc
907176
Процессор текстовых таблиц awk
Команда awk предназначается для обработки текстовых таблиц, столбцы которых разделяются пробельными символами, а строки— символом перевода строки. Язык процессора W:[AWK] использует регулярные выражения RE для выделения строк подлежащих обработке, и подстановочные выражения $1, $2, …, $N для выделения столбцов. Имя процессора образовано от заглавных букв фамилий его разработчиков Aho, Weinberger и Kernighan.
Каждая инструкция языка AWK записывается как /RE/{ ACTION $i…} и заставляет процессор выполнить действие ACTION со столбцами $1 каждой строки, соответствующей регулярному выражению RE.
Так, например, в листинге ниже при помощи awk вместо grep и cut выделяются имена классифицированных командой file файлов, являющихся сценариями командного интерпретатора.
Выборка строк и полей с помощью awk: список имен сценариев интерпретатора
bender@ubuntu:~$ file -Li /*bin/* /usr/*bin/* | awk -F: ‘/shellscript/ { print $1 }’
/bin/bzcmp
/bin/setupcon
/usr/sbin/upgrade-from-grub-legacy
В примере из листинга ниже при помощи awk подсчитывается суммарное потребление RSS-памяти процессами chromium при помощи простейшей AWK-программы с тремя инструкциями. Инструкция begin { sum = 0 } выполняется до анализа строк, инструкция END { print sum } — после анализа всех строк, а инструкция /chromium/ { sum += $i } выполняется при анализе строк и прибавляет к значению переменной sum целочисленное значение первого столбца $1 тех строк, в содержимом которых будет найдена строка chromium.
Суммирование по строкам и полям: суммарная RSS-память процессов браузера chromium
bender@ubuntu:~$ ps axo rss,comm | ↵
> awk ‘BEGIN { sum = 0 } /chromium/ { sum += $1 } END { print sum }’
907176
Потоковый редактор текста sed
Потоковый редактор sed (stream editor) применяется для пакетного (неинтерактивного) редактирования текстовых файлов, имеющих строчную структуру. Инструкции языка W:[SED] используют регулярные выражения RE для выделения строк, подлежащих редактированию при помощи команд CMD: вставки и добавления строк i (insert) и a (append), замены строк с (change), удаления строк d (delete), замены подстрок % (substitute) и др.
В примере из листинга ниже при помощи редактора sed эмулируется поведение grep путем удаления (команда d) тех строк потока, которые не (отрицание регулярного выражения !) содержат подстроку chromium.
Выборка строк: удаление лишних
bender@ubuntu:~$ ps axo rss,comm | sed ‘/chromium/!d’
296628 chromium-browse
84872 chromium-browse
68028 chromium-browse
Аналогично, в листинге ниже фильтруются строки файла /etc/wgetrc путем удаления из вывода строк, не соответствующих регулярному выражению ^[^#] (в начале строки содержащих знак, не похожий на знак комментария #).
Выборка строк: фильтрация комментариев
bender@ubuntu: ~$ sed ‘/’^[^#]/!d* /etc/wgetrc
passtve_ftp = on
Кроме выбора отдельных строк инструкциями вида /RE/CMD, sed позволяет адресовать блоки строк, подлежащих редактированию. При помощи инструкций вида /RE1/,/RE2/CMD команда редактирования применяется , ко, всем строкам, начиная с той, которая соответствует выражению RE1, заканчивая той, которая соответствует выражению RE2.
Так, например, в листинге ниже из потока вывода команды lspci выделяется блок текста, начиная со строки, содержащей слово Ethernet, вплоть до следующей пустой строки (^$ между началом и концом которой не содержится ни одного символа).
Выборка блоков текста
bender@ubuntu:~$ lspci -v | sed ‘ /Ethernet/, /^$/!d’
02:06.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL8111/8168B … (rev 06)
Subsystem: Samsung Electronics Co Ltd Device c0b6
Flags: bus master, fast devsel, latency 0, IRQ 41
I/O ports at 2000 [size=256]
Memory at C0404000 (64-bit, prefetchable) [size=4K]
Memory at C0400000 (64-bit, prefetchable) [size=16K]
Capabilities: <access denied»
Kernel driver in use: r8169
Kernel nodules: r8169
В листинге ниже показан типичный пример самого распространенного варианта использования sed с командой s — замены одних подстрок другими. При помощи трех нехитрых подстановок отсортированный командой sort вывод команды find превращается в дерево файлов и каталогов, аналогичное тому, что строит команда tree.
Команда подстановки подстрок имеет форму /RE/s/FND/RPL/F, при этом во всех строках, соответствующих RE, будут найдены подстроки, соответствующие FND, и заменены на RPL согласно флагам замены F. Регулярное выражение RE может отсутствовать, тогда замены производятся во всех строчках, а регулярные выражения FND и RPL могут быть ограничены любым другим символом, например : вместо /.
Флаги уточняют место замены. Например, флаг g (global) требует произвести столько замен, сколько будет найдено слева направо соответствий выражению FND, а флаг i (case insensitive) указывает на нечувствительность выражения FND к регистру букв.
Таким образом, подстановка s:^..:: выполняет удаление ^.. двух любых символов в начале строки (замену на «ничто»). Это отрежет ненужные префиксы ./ относительных путевых имен, выводимых как результат find.
Подстановка s:[^/.][^/]*+—&: заменяет подстроки, состоящие из любого символа кроме слеша и точки [^/.] и следующих за ним любого количества * любых кроме слеша [^/] символов вплоть до конца строки $, на то же самое значение а, перед которым поставлены символы +—.
Такое действие изобразит имена найденных find файлов и каталогов как +— file. Последняя подстановка s:[^/.]*/:| g глобально заменит подстроки из любого количества * символов кроме слеша [А/], заканчи— вающиеся слешем /, на вертикальную черту с тремя пробелами | . что заменит элементы путевого имени найденных файлов в нужное количество уровней иерархии дерева в выводе.
Замена подстрок: дерево файлов и каталогов
bender@ubuntu:~$ cd /lib/terminfo
bender@ubuntu:/lib/terrninfo$ find . | sort -f
> sed -e ‘s:^..::’ -e ‘s:[^/.][^/]*$:+— &: ‘ -e ‘s[^:]*/:| :g’
.
+— a
| +— ansi
+— c
| +— cons25
+— v
| +— vt100
| +— vt102
| +— vt220
| +— vt52
+— x
| +— xterm
| +— xterm-vt220
| +— xterm-xfree86
Еще один типичный пример использования потокового редактора sed проиллюстрирован ниже, где во всех модулях некоторой программы на языке W: [python] имя модуля module нужно заменить его новым именем Module.
Найденные командой find имена модулей *.ру проверяются командой grep на предмет наличия в их тексте слова (-w) module, затем их имена (-l) отправляются потоковому редактору, который выполняет замену подстрок, замещая (-l) содержимое непосредственно обрабатываемого файла. Замены производятся в строках, содержащих слово inport, при этом только подстроки module, слева \< и справа \> от которых находятся границы слов, т. е. только слова module, заменяются на Module.
Массовая замена подстрок в файлах
bender@ubuntu:~$ find . -name ‘*.ру* | xargs grep -wl module |
> xargs sed -i ‘/import/ s:\<module\>:Module:g’
В очень большом количестве случаев sed применяется для редактирования конфигурационных файлов системы, например в сценариях установки и удалений пакетов программного обеспечения.
В примере из листинга ниже показан небольшой сценарий интерпретатора, позволяющий «включать»/«выключать» параметры в конфигурационном файле /etc/sysctl.conf, используя удаление/установку символа комментария # в начало строк при помощи sed. Для этого в трех инструкциях sed используются две команды подстановки s и команда ветвления t (test).
Первая подстановка s/*#// удаляет комментарий из начала строки, а вторая подстановка s/^/#/ добавляет его в начало. Команда t тестирует результат выполнения первой инструкции подстановки и в случае успеха передает управление на конец списка инструкций.
В результате выполнения инструкций либо комментарий удаляется из начала строки первой инструкцией, если он там присутствует (на чем список инструкций завершается), либо комментарий добавляется в начало строки второй инструкцией.
Редактирование конфигурационных файлов
bender@ubuntu:~$ grep net.ipv4.ip_forward /etc/sysctl.conf
#net.ipv4.ip_forward=1
bender@ubuntu:~$ sudo sed -1 ‘/net.ipv4.lp_forward/ s/*#//’ /etc/sysctl.conf
bendergubuntu:~$ grep net.tpv4.lp_forward /etc/sysctl.conf
net. ipv4.ip_forward=1
bender@ubuntu:~$ cat /usr/local/bin/toggle-comment
#!/bin/sh
[ -n «$i» -a -f «$2» ] &&
sed -i -e «/$1/ s/^#//» -et-e «/$1/ s/^/#/» $2 ||
echo «Usage: toggle-coment RE file»
bender@ubuntu:~$ sudo toggle-coment net.ipv4.ip_forward /etc/sysctl.conf
bender@ubuntu:~$ grep net.ipv4.ip_forward /etc/sysctl.conf
#net. ipv4. ip_forward=1
Несмотря на простоту инструментальных средств обработки текста, таких как awk и sed, энтузиасты реализуют с их помощью (вкупе с управляющими ESC-последовательностями терминала и символами кодировки utf-8) даже простые игры, такие как крестики-нолики (tic-tac-toe), тетрис (tetris), sokoban, arkanoid, 2048 и даже (!) шахматы.
Более подробное изложение концепций и практик применения регулярных выражений, sed(1) и awk(1), можно найти в книгах ISBN: [1-56592-225-5], ISBN:[5-93286-121-5], а полноценный курс программирования на я.зыке командного интерпретатора — в ISBN: [5-7315-0114-9] и ISBN:[5-932864)29-4].