Инструментальные средства обработки текста в ОС Linux

Данные, которые генерируют, обрабатывают и потребляют внешние и встроенные команды и конструкции интерпретатора, представляют собой текстовые потоки с произвольной структурой. Чаще всего в потоке можно выделить строки, отделяемые друг от друга управляющим символом перевода строки \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].

Добавить комментарий