Awk — a pattern scanning and processing language
SED — Stream EDitor
AWK & SED
Оглавление страницы
Передача переменных утилите awk
Использование переменной FS в сценариях awk
Передача переменных сценариям awk
Чтение и обработка данных в sed
Правила отбора строк в редакторе sed:
Основные команды редактирования
Вывод номеров строк (команда =)
Отображение строк из заданного диапазона
Поиск строк, соответствующих шаблону
Поиск пo шаблону и номеру строки
Ссылка на искомую подстроку с помощью метасимвола &
Чтение строк из файла (команда r)
Вывод строк в файл (команда w)
Досрочное завершение работы (команда q)
Отображение управляющих символов (команда l)
Обработка управляющих символов
Отображение отдельных полей всех записей
Обработка сообщений об ошибках
Проверки на несовпадение и неравенство
Операторы присваивания и арифметические операторы
Изменение значения числового поля
Изменение значения текстового поля
Отображение только измененных записей
Комбинировать строки с полями:
Встроенные функции работы со строками
Передача строк из интерпретатора shell утилите awk
Задачи обработки-фильтрации текста естественным и логичным образом являются приоритетными для администрирования *nix систем, кроме семейства команд обработки текста типа: grep, sort, tsort, uniq, past, join, tail и многих других, владение утилитами обработки текста как SED и AWK, необходимое условие профессионализма сисадминистратора, при помощи этих гибких и эффективных инструментов возможно создание как сложных скриптов так и однострочных команд (expressions) по не интерактивному редактированию проходящему через них потоку текста. https://vds-admin.ru/sed-and-awk
Поток данных (stream) в программировании — абстракция, используемая для чтения или записи файлов, сокетов и т. п. в единой манере. Потоки являются удобным унифицированным программным интерфейсом для чтения или записи файлов (в том числе специальных и, в частности, связанных с устройствами), сокетов и передачи данных между процессами. Поддержка потоков включена в большинство языков программирования.
При создании любого процесса ядро создает три стандартных потока:
поток ввода (stdin), файловый дескриптор Ø
поток вывода (stdout), файловый дескриптор 1
поток вывода ошибок (stderr), файловый дескриптор 2
Поток ввода открыт на чтение, потоки вывода — на запись. По умолчанию поток ввода связан с клавиатурой, потоки вывода — с дисплеем
Возможность перенаправления потоков позволяет связывать различные программы, и придаёт системе гибкость, являющуюся частью философии Unix.
Убедиться в том что это чистая правда можно выполнив в терминале следующую не хитрую команду:
$ ls -lah /proc/$$/fd/
total 0
dr-x------ 2 0 Jul 27 21:54 ./
dr-xr-xr-x 9 0 Jul 27 21:52 ../
lrwx------ 1 64 Jul 27 21:54 0 -> /dev/pts/0
lrwx------ 1 64 Jul 27 21:54 1 -> /dev/pts/0
lrwx------ 1 64 Jul 27 21:54 2 -> /dev/pts/0
lrwx------ 1 64 Jul 27 21:54 255 -> /dev/pts/0
$ ..зачем открыт файл с дескриптором 255 .... никто не знает, но это и не важно.
Так еще интересней:
$ ls -lah /proc/self/fd/
total 0
dr-x------ 2 0 Dec 17 18:40 ./
dr-xr-xr-x 9 0 Dec 17 18:40 ../
lrwx------ 1 64 Dec 17 18:40 0 -> /dev/pts/0
lrwx------ 1 64 Dec 17 18:40 1 -> /dev/pts/0
lrwx------ 1 64 Dec 17 18:40 2 -> /dev/pts/0
lr-x------ 1 64 Dec 17 18:40 3 -> /proc/144823/fd/
/proc/self@ является символической ссылкой на каталог, соответствующий текущему процессу,
$ ls -l /proc/self
lrwxrwxrwx 1 root root 0 Dec 19 2022 /proc/self -> 9053/
, естественно содержимое ссылки меняется в зависимости от того, кто к ней обращается.
Тоже не плохой пример демонстрирующий как конструкция "herestring" передает строку в stdin, не создавая subshell:
$ ls -l <<< cd /proc/self/fd
total 0
lr-x------ 1 64 Dec 20 14:47 0 -> 'pipe:[198454]' #канал в stdin, и ни каких subshell
lrwx------ 1 64 Dec 20 14:47 1 -> /dev/pts/0
lrwx------ 1 64 Dec 20 14:47 2 -> /dev/pts/0
lr-x------ 1 64 Dec 20 14:47 3 -> /proc/47541/fd/
Б. Керниган, Д. Ритчи, - Язык программирования С << ----- ст. 230 PDF
N. Wirth Algorithm + Data structure = program << ----- PDF почти филосовский трактат
Данные — абстракция некоторого фрагмента "видимого" мира.
Поток — это источник или получатель данных связанный с диском или с каким-то другим внешним устройством. Библиотека поддерживает два вида потоков: текстовый и бинарный, хотя на некоторых системах, в частности в UNIXe, они не различаются. Потоковый ввод-вывод
Текстовый поток — это последовательность строк; каждая строка имеет от нуля или более символов и заканчивается управляющем символом '\n' - символом конца строки, либо перевода строки. Символы текста кодируются разными значениями байтов или их последовательностями. Информация о том, как именно эти символы должны располагаться на странице (форматироваться), тоже кодируется с помощью неотображаемых управляющих символов, типа конца строки – ASCII 10 или символа табуляции – ASCII 9,11. В простейших случаях число управляющих кодов ограничивается 32-мя первыми значениями байта (или кода ASCII), а все остальные значения байта используются для кодирования информационных символов, текстовых символов. Именно такие файлы называются ASCII-файлами или "текстовыми". Структурированный текст - это текст ASCII (American Standard Code for Information Interchange), который состоит из полей со значениями и разделителей полей, которые обязательно присутствует в структуированном файле, в качестве разделителя полей могут использоваться различные символы, обычно это space – ASCII 32. ASCII – не единственный, но самый простой возможный текстовый формат, так как он состоит только из текстовых символов и не содержат шрифтов, стилей, цветов и прочего.
Примерами таких файлов могут служить файлы, создаваемые редакторами типа: ed, vi-vim, редактором mc и прочих, естественно кроме создаваемых пользователем текстовых файлов существует масса системных, созданных и форматированных по тем же алгоритмам, потому наличие текстового редактора, как и утилит умеющих обрабатывать эти ASCII – файлы, задача первоочередная для *nix-систем, и как следствие по прежнему не утихает религиозная война между адептами vim и emacs.
Бинарный поток — это последовательность не преобразованных байтов, представляющих собой некоторые промежуточные данные, которые обладают тем свойством, что если их записать, а затем прочесть той же системой ввода-вывода, то мы получим информацию, совпадающую с исходной.
Поток, процесс соединяется с файлом или устройством посредством его открытия и идентифицируется системой посредствам (в конечном итоге) файловых дескрипторов, указанная связь разрывается путем закрытия потока, процесса. Открытие файла возвращает указатель на объект типа "FILE", который содержит всю информацию, необходимую для управления этим потоком.
Точнее так: В составе стандартной библиотеки C есть стандартная библиотека ввода-вывода, сокращенно именуемая stdio.
Указатели файлов: Процедуры стандартного ввода-вывода не работают непосредственно с файловыми дескрипторами, вместо этого каждая использует свой уникальный идентификатор, обычно называемый "указателем файла". В библиотеке C указатель файла ассоциируется с файловым дескриптором то есть отображается на него. Указатель файла представлен как указатель на "определение типа" FILE, определяемый в <stdio.h>, Название FILE записывается прописными буквами по тому что изначально стандартный ввод вывод это "макрокоманда", По мере того как язык C развивался то и стандартный ввод-вывод был регламентирован как официальная часть языка, большинство методов были переписаны в виде обычных функций, а FILE стал "определением типа", но он так и продолжает записываться в верхнем регистре. В терминологии ввода-вывода открытый файл называется потоком данных.
При старте операционной системы создается что то порядка 12-и открытых файлов три из них зарезервированы для стандартных потоков ввода, вывода и ошибок:
stdin(fd0), stdout(fd1) и stderr(fd2). Стандартный ввод-вывод
Чтобы напечатать управляющие символы есть утилита cat c указанием опции -v, cat выводит эти символы в нотации "с крышкой'', т.е. они представляются сочетанием `^' и символа, соответствующего управляющему символу (^C - прервать),
Чтобы визуально отобразить конец строки в каждой строке, используйте опцию -E; она приведет к тому, что в конце каждой строки появится символ $.
Полезной является опция -T, которая выводит все табуляции в виде ^I.
Ключ -A комбинирует все три вышеуказанные опции - как если бы Вы указали опции с помощью -vET.
PS: Если в VIM поместить курсор на символ и нажать сочетание ga, можно увидеть его код в строке состояния. Например: ascii [+]: <A> 65, Hex 41, Octal 101
VIM как hex/octal editor:
hex – :%!xxd (вернуться :%!xxd -r)
octal – :%!od
: –входит в режим командной строки
% –сопоставляет весь файл как диапазон,
! –фильтрует этот диапазон с помощью внешней команды,
xxd как и od , как и hd это внешниe команды оболочки, после внесения изменений можно вернуться к тексту с помощью ключа -r (команды xxd), поэтому: :%!xxd -r
$ type xxd; xxd is /usr/bin/xxd
$ type od; od is /usr/bin/od
$ type hd; hd is /usr/bin/hd
Command Line Tools to dump files in hex,octal and bin formats
SED: Stream EDitor — потоковый текстовый редактор как и AWK с полным на то основанием можно называть языком программирования, не интерактивный, построчный редактор текстовых файлов потокового типа, Sed получает входной поток данных или файл построчно, редактирует каждую строку согласно правилам, определённым в sed-скрипте, и затем выводит результат, текст принимается либо с устройства stdin, либо из текстового файла, после чего выполняются заданные операции над строками и выводится результат на устройство stdout или в файл. Резюмируя можно привести выдержку из отличной книги "Девид Тейнсли Linux и Unix Програмирование в Shell": Linux и UNIX: программирование в shell. Руководство разработчика.fb2
"Отличительной особенностью редактора sed является то, что он позволяет модифицировать файлы в фоновом режиме. Задача пользователя сводится к тому, чтобы задать последовательность команд редактирования, а всю остальную работу выполнит сам редактор. Кроме того, sed допускает внесение всех изменений за один проход, а это превращает его в достаточно эффективный и, что важнее всего, быстродействующий инструмент редактирования текста, быстродействие достигается в следствии нескольких особенностей редактора, первая то, что он работает с "потоками" по сути средствами ядра операционной системы(и здесь надо вернуться к понятию "поток"),
вторая это инвертированность(перевернутость) по отношению к тексту и набору команд редактирования, sed вначале загружает в себя набор команд, а затем применяет весь набор команд к каждой строке текста и по скольку одновременно в памяти находится только одна строка, sed может обрабатывать "произвольно большие" текстовые файлы — данные
AWK: Язык обработки шаблонов с C-подобным синтаксисом, утилита AWK с полным на то основанием считается наиболее "продвинутым" средством по формированию отчетов и извлечению информации из больших текстовых файлов, что и неудивительно поскольку говоря про AWK на самом деле придется говорить не об одной из многочисленных утилит *nix систем, а о совершенно самостоятельном языке программирования, языке AWK, в чем собственно и скрывается не которая трудность в его или ее изучении. Примеров использования, шаблонов, скриптов, как и областей применения громадное множество, ниже, в большом количестве приводятся выдержки, цитаты и скрипты из выше упомянутой великолепной книги Дэвида Тейнсли, в книге сделан акцент на умение создавать эффективные одно-строчные команды и небольшие сценарии, выполняющие фильтрацию текстовых файлов по средствам AWK и SED, как раз то, что и необходимо в текущей работе по администрированию *nix систем.
http://www.commandlinefu.com/commands/using/awk — это место, где можно записать те жемчужины командной строки, к которым вы возвращаетесь снова и снова.
http://www.lissyara.su/doc/programming/awk/ <<------------
http://citforum.ru/operating_systems/unixuser/gl10_1.shtml
https://www.opennet.ru/docs/RUS/bash_scripting
https://www.opennet.ru/base/dev/sed1line.txt.html <<-----------Полезные одно-строчные скрипты sed
http://dbserv.pnpi.spb.ru/~shevel/Book/node67.html <<------------
https://life-prog.ru/MAN AWK.html
"Девид Тейнсли Linux и Unix Програмирование в Shell" <<--------- PDF
"Девид Тейнсли Linux и Unix ........" <<--------PDF (другой вариант, местами исправленный)
Aho — Kernighan — Weinberger: "The AWK Programming Language" <<--------- "первоисточник"
Уроки по Awk <<--------------
AWK
Название утилиты AWK составлено из начальных букв фамилий разработчиков языка: Ахо (Aho), Вайнбергера (Weinberger) и Кернигана (Kernighan). Существуют также утилиты nawk и gawk, обладающие усовершенствованными возможностями обработки текста. Вызов утилиты awk возможен тремя способами:
Первый заключается в передаче ей требуемых команд непосредственно в командной строке:
awk [-F разделитель_полей] 'сценарий' входной_файл…
В одинарных кавычках (про которые ясно дело не стоит забывать) указывается список инструкций языка awk. Задавать разделитель полей с помощью опции -F не обязательно, так как по умолчанию утилита awk использует для этих целей пробел, и конечно при условии что в фильтруемом источнике
так-же использован пробел. Но, например, в файле /etc/passwd поля отделяются друг от друга двоеточием и в этом случае вызов утилиты выглядит так:
awk -F: 'сценарий' входной_файл…
Второй способ вызова утилиты awk состоит в создании отдельного файла сценария, содержащего список инструкций awk. При этом утилита awk указывается в первой строке сценария в качестве интерпретатора команд. Созданный файл делается исполняемым и может быть вызван из командной строки.
Третий способ, способ когда все инструкции awk помещаются в отдельный файл, после чего осуществляется вызов этого файла:
awk -f файл_сценария входной_файл…
Опция -f свидетельствует о том, что инструкции awk содержатся в файле сценария. Утилита анализирует информацию, содержащуюся в одном или нескольких входных файлах.
Сценарии
Сценарий awk — это набор инструкций, состоящих из шаблонов и связанных с ними процедур. Когда утилита просматривает записи входного файла, она проверяет, установлена ли опция -F или переменная FS (о чем поясняется позже), задающие разделители полей записи. По умолчанию в качестве разделителя принят пробел. При обнаружении символа новой строки прочитанная строка классифицируется как запись, и к ней применяются инструкции сценария. Процесс чтения записей продолжается до тех пор, пока не будет обнаружен признак конца файла. Возможны комментарии (как в shell "#.........").
В таблице ниже приведен образец входного файла и продемонстрировано, как утилита awk его анализирует. Утилита последовательно просматривает строки файла. Отыскав первый символ-разделитель, она помечает все предыдущие символы как [Поле 1]. Символы между первым и вторым разделителями обозначаются как [Поле 2] и т. д,
Команду awk в shell возможно представить как команду контектсного поиска и преобразования текста, это суть фильтр, суть оболочка awk в оболочке shell. Процесс анализа завершается при обнаружении символа новой строки, который по умолчанию считается признаком конца записи. После этого содержимое полей сбрасывается и утилита переходит к следующей строке файла.
Образец анализа входного файла:
Инструкция, или код операции awk состоит из шаблона (pattern) и связанной с ним процедуры (action), причем количество инструкций в сценарии awk может быть очень велико.
Pattern (шаблон) — Шаблонная часть уточняет, когда следует выполнять инструкцию либо к каким входным данным она должна применяться, можно сказать что это "устоявшаяся" конструкция при помощи которой фильтруется выходной поток. Шаблоном может служить любая условная или составная конструкция, или регулярное выражение.
Action (процедура) — Процедура определяет порядок обработки данных, состоит из последовательности операторов, разделённых точкой с запятой (;), символом перевода строки или закрывающейся скобкой.
шаблон {процедура}, или шаблон — когда выводятся строки с данным шаблоном, или {процедура} — когда действие выполняется для всех строк. The Structure of an AWK Program — ст.14
PS: Возможно не будет не правдой сказать, что понятие "pattern - шаблон" восходит ко временам формирования языков и на том этапе основным средством обработки текстов или потоков были регулярные выражения(по сути им и остаются), которые представляют собой набор шаблонов, состоящих как из специальных, так и обычных символов, поэтому понятие "шаблон" и "рег. выражение" близки между собой.
Существует также два специальных шаблона: BEGIN и END;
Шаблон BEGIN применяется для инициализации переменных и создания заголовков отчета. Связанная с ним процедура выполняется перед началом обработки входного файла.
Шаблон END употребляется для вывода итоговых данных и выполнения инструкций по завершении обработки файла, они часто называются как операторы специального вида: BEGIN — начальные установки, END — действия, выполняемые после обработки последней записи входного потока.
Синтаксис базовых "Регулярных Выражений" — шаблонов, расширен следующими дополнениями:
"()" — скобки для группирования РВ;
"|" — логическое "или";
"+" — плюс, стоящий за РВ, означает любую последовательность вхождений этого РВ, начиная с первого;
"?" — знак вопроса, стоящий за РВ, означает 0 или 1 вхождений этого РВ.
Если никакой шаблон не указан, процедура выполняется для каждой записи из входного файла. {Тело процедуры заключается в фигурные скобки}. Чаще всего процедура осуществляет вывод информации на экран, но она может также содержать операторы присваивания, управляющие конструкции и встроенные функции. Если процедура не задана, утилита awk выводит на экран все содержимое записи, соответствующей шаблону.
Поля и записи
На поля текущей записи можно ссылаться следующим образом: $1, $2… $n. Этот метод называется идентификацией полей. Подобная схема обозначений значительно облегчает работу с полями. Например, в качестве ссылки на первое и третье поля достаточно указать:
$1, $3
Обратите внимание на то, что идентификаторы полей разделяются запятой. Чтобы сослаться на все поля записи, содержащей пять полей, можно указать:
$1, $2, $3, $4, $5
Однако проще воспользоваться идентификатором $0, который служит для обозначения всех полей текущей записи.
Когда в процессе просмотра утилита awk встречает символ новой строки, считается, что найден конец записи, после чего выполняется переход к новой строке, анализ новой записи и инициализация полей. Чтобы вывести на экран содержимое записи, в теле процедуры задается команда print со списком нужных полей.
Тестовый файл
AWK как и SED прежде всего "потоковые" редакторы, но для практическо-наглядной части, создадим файл например "grade.txt", на котором и будет основано большинство примеров. Файл этот должен содержать несколько записей из локальной базы данных секции каратистов (таков выбор m.Teynsli).
$ ~ cat > grade.txt
M.Tansley 05/99 48311 Green 8 40 44
N.Lulu 06/99 48317 green 9 24 26
O.Bunny 02/99 48 Yellow 12 35 28
P.Troll 07/99 4842 Brown-3 12 26 26
Q.Tansley 05/99 4712 Brown-2 12 30 28
# разделитель <Space>
^C
$ ~
Назначение полей таково:
1 — имя;
2 — дата получения пояса;
3 — порядковый номер ученика;
4 — полученный пояс;
5 — возраст;
6 — текущий рейтинг;
7 — максимальное количество рейтинговых очков, которое можно было получить за участие в прошедшем соревновании.
Поля разделяются пробелами, поэтому при вызове утилиты awk можно не указывать опцию -F.
Изменить разделитель полей:
$ awk '$1=$1' FS=" " OFS=":_" grade.txt
M.Tansley:_05/99:_48311:_Green:_8:_40:_44
N.Lulu:_06/99:_48317:_green:_9:_24:_26
O.Bunny:_02/99:_48:_Yellow:_12:_35:_28
P.Troll:_07/99:_4842:_Brown-3:_12:_26:_26
Q.Tansley:_05/99:_4712:_Brown-2:_12:_30:_28
#:_разделитель:_<Space>
Соеденить строки используя sed:
$ cat grade.txt
M.Tansley 05/99 48311 Green 8 40 44
N.Lulu 06/99 48317 green 9 24 26
O.Bunny 02/99 48 Yellow 12 35 28
P.Troll 07/99 4842 Brown-3 12 26 26
Q.Tansley 05/99 4712 Brown-2 12 30 28
# разделитель <Space>
$ sed -e :a -e '/$/N;s/\n/ /;ta' grade.txt
M.Tansley 05/99 48311 Green 8 40 44 N.Lulu 06/99 48317 green 9 24 26 O.Bunny 02/99 48 Yellow 12 35 28 P.Troll 07/99 4842 Brown-3 12 26 26 Q.Tansley 05/99 4712 Brown-2 12 30 28 # разделитель <Space>
В примере слева, мы редактируем "поток" (естественно), что бы отредактировать файл его надо переписать:
$ awk '$1=$1' FS=" " OFS=":_" grade.txt > tmpfile; mv tmpfile grade.txt
$ cat grade.txt
M.Tansley:_05/99:_48311:_Green:_8:_40:_44
N.Lulu:_06/99:_48317:_green:_9:_24:_26
O.Bunny:_02/99:_48:_Yellow:_12:_35:_28
P.Troll:_07/99:_4842:_Brown-3:_12:_26:_26
Q.Tansley:_05/99:_4712:_Brown-2:_12:_30:_28
#:_разделитель:_<Space>
Сохранение выходных данных
Напомним, что существует два способа, позволяющих сохранить результаты работы утилиты awk. Первый, более простой, способ состоит в том, чтобы перенаправить выходной поток в требуемый файл, при этом выходные данные не будут отображаться на экране:
$ awk '{print $0}' grade.txt > grade.out
Второй способ заключается в применении команды tee - тройник. Выходные данные передаются в файл и одновременно отображаются на экране. Например:
$ awk '{print $0}' grade.txt | tee grade.out
Отображение всех записей
В приведенной ниже команде утилита awk просматривает файл grade.txt и, поскольку шаблон не указан, отображает содержимое всех записей:
$ ~ awk '{print $0}' grade.txt
M.Tansley 05/99 48311 Green 8 40 44
N.Lulu 06/99 48317 green 9 24 26
O.Bunny 02/99 48 Yellow 12 35 28
P.Troll 07/99 4842 Brown-3 12 26 26
Q.Tansley 05/99 4712 Brown-2 12 30 28
$ ~
DU:
# du -sb * | sort -nr | head | awk '{print $2}' | xargs du -sh
1.6G VMware
1.2G GNS3
783M Desktop
548M VMware-Workstation-Full-16.0.0
191M Videos
156M Видео
119M Telegram
140M myapp_postgre
83M HP
57M hplip-3.21.12
# cd /var
# du -sb * | sort -nr | head | awk '{print $2}' | xargs du -sh
5.8G lib
1.3G log
226M cache
25M backups
13M www
7.8M spool
2.9M mail
2.0M NX
76K tmp
4.0K opt
Так тоже не плохо:
$ du -x ${SYMLINKS} -k "$@" | sort -n | tail -100 | awk 'BEGIN { logx = log(1024); size[0]= "KB"; size[1]= "MB"; size[2]= "GB"; size[3]= "TB" } { x = $1; $1 = ""; v = int(log(x)/logx); printf ("%8.3f %s\t%s\n",x/(1024^v), size[v], $0) }'
Перечислить все разделы:
$ awk '/d.[0-9]/{print $4}' /proc/partitions
sda1
sda2
sda3
sda4
sda5
sda6
sda7
sda8
$ cat /proc/partitions
major minor #blocks name
8 0 732574584 sda
8 1 40131 sda1
8 2 12843008 sda2
8 3 193784216 sda3
8 4 1 sda4
8 5 351087616 sda5
8 6 132285440 sda6
8 7 40369152 sda7
8 8 2158592 sda8
11 0 1048575 sr0
Просто размеры разделов;
$ lsblk | grep -v part | awk '{print $1 "\t" $4}'
NAME SIZE
loop0 4K
loop1 55.7M
loop2 105.8M
loop3 140K
loop4 91.7M
loop5 243.7M
sda 698.6G
sr0 1024M
Общий размер каждого подкаталога KB, MB, GB, TB:
$ du --max-depth=1 | sort -nr | awk ' BEGIN { split("KB,MB,GB,TB", Units, ","); } { u = 1; while ($1 >= 1024) { $1 = $1 / 1024; u += 1 } $1 = sprintf("%.1f %s", $1, Units[u]); print $0; } '
4.6 GB .
3.2 GB ./downloads
669.6 MB ./.cache
485.5 MB ./.config
127.5 MB ./.local
61.6 MB ./.cr3
34.2 MB ./vmware-host-modules
.........................
Убить все процессы юзера
$ ps -fu $USER | awk {'print $2'} | xargs kill [-9]
Отображение отдельных полей всех записей
Предположим, требуется отобразить на экране только имена спортсменов и названия поясов, которыми они обладают. Соответствующие данные хранятся в полях $1 и $4, поэтому введем такую команду:
$ ~ awk '{print $1, $4}' grade.txt
M.Tansley Green
N.Lulu green
O.Bunny Yellow
P.Troll Brown-3
Q.Tansley Brown-2
LAN IP:
$ ifconfig | grep broadcast | awk '{print $2}'
172.16.193.1
172.16.11.1
192.168.1.201
$ ip a s wlan0 | awk -F'[/ ]+' '/inet[^6]/{print $3}'
192.168.1.201
Или так:
$ ifconfig | awk '/inet / {sub(/addr:/, "", $2); print $2}'
127.0.0.1
192.168.228.1
172.16.22.1
192.168.1.8
Или так (только IP):
$ hostname -I | awk '{print $1}'
192.168.1.12
Узнать внешний IP-адрес маршрутизатора, можно с помощью "майора Хайдена" командой:
$ wget -O - -q icanhazip.com
$ curl -s icanhazip.com
*** The original icanhazip.com lives on, but the other services are going offline.😢(28.07.22)
Другой вариант узнать внешний адрес;
$ curl -s 'http://checkip.dyndns.org' | sed 's/.*Current IP Address: \([0-9\.]*\).*/\1/g'
178.66.176.70
Напечатать nickname wireless:
$ nmcli -g name,type connection show --active | awk -F: '/ethernet|wireless/ { print $1 }'
myname
DF:
# df / | awk '{print $1}' | grep dev | xargs tune2fs -l | grep create
Filesystem created: Thu Dec 7 16:52:00 2017
Найти роутер:
$ awk 'NR==2 {print $1}' /proc/net/arp
192.168.1.1
Так то же неплохо:
$ /sbin/route -n | grep "^0\.0\.0\.0" | awk '{ print $2 }'
192.168.1.1
Распечатайте свои mac адреса:
$ ifconfig -a | sed '/eth\|wl/!d;s/ Link.*HWaddr//'
ether d4:be:d9:5a:b0:3b txqueuelen 1000 (Ethernet)
ether 00:50:56:c0:00:01 txqueuelen 1000 (Ethernet)
ether 00:50:56:c0:00:08 txqueuelen 1000 (Ethernet)
wlp8s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>
mtu 1500
ether 68:5d:43:eb:05:0a txqueuelen 1000 (Ethernet)
Функция:
lsmac() { ifconfig -a | sed '/eth\|wl/!d;s/ Link.*HWaddr//' ; }
Разделы использующие более 50%(или сколько надо) дискового пространства:
$ df -h |awk '{a=$5;gsub(/%/,"",a);if(a > 50){print $0}}'
Filesystem Size Used Avail Use% Mounted on
/dev/sda10 77G 61G 12G 85% /Data
/dev/sda5 124G 106G 12G 90% /Share
Default gateway:
$ ip route | awk '/default/{print $3}'
192.168.1.1
Когда установлена ОСь.
$ ls -lct /etc/ | tail -1 | awk '{print $6, $7, $8}'
Dec 7 2017
$ ls -ldct /lost+found | awk '{print $6, $7, $8}'
Dec 7 2017
Название дистрибютива:
$ cat /etc/issue
Debian GNU/Linux 12 \n \l
Лучше так:
$ lsb_release -a
No LSB modules are available.
Distributor ID: Debian
Description: Debian GNU/Linux 12 (bookworm)
Release: 12
Codename: bookworm
Количество свободной памяти:
$ grep '^MemFree:' /proc/meminfo | awk '{ mem=($2)/(1024) ; printf "%0.0f MB\n", mem }'
1062 MB
$ free -m | awk '/Mem/ {print $4}'
1056
$
Найти самый новый файл в каталоге:
$ find ~/ -type f -printf "%T@|%p\n" 2>/dev/null | sort -n | tail -n 1| awk -F\| '{print $2}'
/home/user/.config/chromium/Profile 3/History-journal
Вывести используемый интерфейс:
$ ip addr | awk '/state UP/ {print $2}' | sed 's/.$//'
wlp8s0
или:
$ route | grep -m1 ^default | awk '{print $NF}'
wlp8s0
Распечатать все интерфейсы:
$ awk '{print $1}' /proc/net/dev|grep :|sed "s/:.*//g"
lo
enp7s0
wlp8s0
vmnet1
vmnet8
Удалить все неиспользуемые ядра:
$ sudo aptitude remove $(dpkg -l | egrep '^ii linux-(im|he)' | awk '{print $2}' | grep -v `uname -r`);echo -e "Current version: \c"; uname -r
No packages will be installed, upgraded, or removed.
0 packages upgraded, 0 newly installed, 0 to remove and 1195 not upgraded.
Need to get 0 B of archives. After unpacking 0 B will be used.
Current version: 6.0.0-kali3-amd64
Посмотреть наличные ядра:
$ ls /usr/src
$ ncdu /usr/src/
$ dpkg --list | grep linux-image
Посмотреть количество доступных обновлений для системы
$ apt-get -s upgrade | awk '/[0-9]+ upgraded,/ {print $1 " package updates are available"}'
10 package updates are available
Вывести все пакеты от которых не зависит ни один пакет
$ dpkg-query --show --showformat='${Package}\t${Status}\n' | tac | awk '/installed$/ {print $1}' | xargs apt-cache rdepends --installed | tac | awk '{ if (/^ /) ++deps; else if (!/:$/) { if (!deps) print; deps = 0 } }'
atmel-firmware
base-passwd
bash
bluez-firmware
bsdutils
.........
Вставить строку через определенное количество строк и пронумеровать выделенные области.
$ ls -l | awk '{if (NR % 7 == 0) print "##############################"; print}' | nl -s') ' -b p'#'
total 112
-rw-r--r-- 1 user user 241 Dec 11 15:52 awk
drwxr-xr-x 2 user user 4096 Sep 26 20:46 backup
drwxr-xr-x 2 user user 4096 Sep 26 20:46 bin
-rw-r--r-- 1 user user 0 Dec 10 14:25 cat
-rw-r--r-- 1 user user 6382 Dec 2 20:01 conkyrc
1) ##############################
-rw-r--r-- 1 user user 2404 Oct 8 15:33 connkyrc
-rw-r--r-- 1 user user 0 Dec 8 18:18 curl
drwxr-xr-x 2 user user 4096 Oct 3 12:34 documents
drwxr-xr-x 4 user user 4096 Dec 10 18:58 downloads
-rw-r--r-- 1 user user 241 Dec 10 13:29 echo
-rw-r--r-- 1 user user 0 Dec 8 18:29 exec
-rw-r--r-- 1 user user 2287 Dec 9 03:01 foonction
2) ##############################
-rw-r--r-- 1 user user 241 Dec 10 14:31 ggrade.txt
-rw-r--r-- 1 user user 209 Dec 11 20:11 grade.txt
drwxr-xr-x 3 user user 4096 Dec 18 16:12 images
-rw-r--r-- 1 user user 1424 Oct 8 15:09 logg
-rw-r--r-- 1 user user 0 Dec 6 15:04 ls
-rw-r--r-- 1 user user 132 Nov 29 17:03 mpass
drwxr-xr-x 2 user user 4096 Sep 26 20:46 music
3) ##############################
....следующие семь
PS: Нумерация строк команда — nl nl — нумерация строк
Просто пронумеровать
$ ls -l | sed "/^/=" | sed "N;s/\n/. /"
1. total 21476
2. -rw-r--r-- 1 user user 21846836 Dec 28 2019 4kvideodownloader_4.4.11-1_amd64.deb
3. -rw-r--r-- 1 user user 0 Apr 21 13:30 ask
4. -rw-r--r-- 1 user user 241 Dec 11 15:52 awk
5. drwxr-xr-x 2 user user 4096 Sep 26 2023 backup
6. drwxr-xr-x 2 user user 4096 Sep 26 2023 bin
............................................................................
Печать горизонтальной линии
$ printf "%`tput cols`s"|sed "s/ /_/g"
______________________________________________________________________________
$ printf -v _hr "%*s" $(tput cols) && echo ${_hr// /${1--}}
------------------------------------------------------------------------------
Вывести заданную строку из файла
$ awk 'NR==linenumber' filename
Печатать только четные строки файла
$ awk '{if (NR % 2 == 0) print $0}' filename
Распечатать строки из диапазона строк
$ cat -n grade.txt
1 M.Tansley 05/99 48311 Green 8 40 44
2 N.Lulu 06/99 48317 green 9 24 26
3 O.Bunny 02/99 48 Yellow 12 35 28
4 P.Troll 07/99 4842 Brown-3 12 26 26
5 Q.Tansley 05/99 4712 Brown-2 12 30 28
6 # разделитель <Space>
$ awk 'NR >= 3 && NR <= 6' grade.txt
O.Bunny 02/99 48 Yellow 12 35 28
P.Troll 07/99 4842 Brown-3 12 26 26
Q.Tansley 05/99 4712 Brown-2 12 30 28
# разделитель <Space>
Перечислить самые "жирные" пакеты
$ awk '{if ($1 ~ /Package/) p = $2; if ($1 ~ /Installed/) printf("%9d %s\n", $2, p)}' /var/lib/dpkg/status | sort -n | tail | awk 'BEGIN {print "\nInstalled-Size Package name \n-----------------------------"} {print $1"\t " $2} END {print "\nPS: Installed-Size - Общий объем дискового пространства, \nнеобходимого для установки пакета.\nhttps://www.debian.org/doc/debian-policy/ch-controlfields.html#installed-size"}'
Installed-Size Package name
-----------------------------
117419 libreoffice-core
143823 firmware-netronome
187651 openjdk-17-jre-headless
214208 virtualbox-7.0
227099 chromium
230978 firefox-esr
326369 google-chrome-stable
334238 fonts-noto-extra
398459 linux-image-6.1.0-13-amd64
398647 linux-image-6.1.0-12-amd64
PS: Installed-Size - Общий объем дискового пространства,
необходимого для установки пакета.
https://www.debian.org/doc/debian-policy/ch-controlfields.html#installed-size
Для полноты картины сделать так:
$ apt-cache show libreoffice-core | grep Size
Installed-Size: 117419
Size: 32580380
И понимать разницу, между "размером дискового пространства" и "размером пакета"... и вообще надо сделать не две колонки а три.
Найти новый файл в текущей дирректории
$ > /tmp/newfile
$ cd /tmp
$ find . -type f -printf "%T@|%p\n" 2>/dev/null | sort -n | tail -n 1 | ls -l $(awk -F \| '{print $2}')
-rw-r--r-- 1 user user 0 Dec 6 15:25 ./newfile
Функция:
$ nf () { find . -type f -printf "%T@|%p\n" 2>/dev/null | sort -n | tail -n 1 | ls -l $(awk -F \| '{print $2}'); }
$ nf
-rw-r--r-- 1 user user 0 Dec 6 15:25 ./newfile
PS:awk -F \| ,здесь \| это разделитель полей, что бы вертикальная черта не интерпритировалась как логическое "или" ее надо экранировать, смотри man awk FS(field separator) и вывод команды find. Конечно возможно псевдоним был бы удобнее, но в силу ограничений накладываемых на механизм псевдонимов, возможно только функция, поскольку в отличие от функций, псевдонимы не позволяют осуществлять подстановку значений переменных, в нашем случае это $(awk -F \| '{print $2}').
Список файлов созданных/модифицированных в текущем каталоге сегодня:
$ ls -l --time-style=+%Y-%m-%d | awk "/$(date +'%Y-%m-%d')/ {print \$7}"
$ > /tmp/newfile
$ cd /tmp
$ ls -l --time-style=+%Y-%m-%d | awk "/$(date +'%Y-%m-%d')/ {print \$7}"
newfile
snap-private-tmp
ssh-XXXXXXyFTzFP
systemd-private-093c4643099f44d9abb6e5b0f92f5abc-colord.service-V1ZMK8
systemd-private-093c4643099f44d9abb6e5b0f92f5abc-ModemManager.service-cr9M0i
systemd-private-093c4643099f44d9abb6e5b0f92f5abc-ntpsec.service-xigihj
systemd-private-093c4643099f44d9abb6e5b0f92f5abc-systemd-logind.service-ISSK6n
systemd-private-093c4643099f44d9abb6e5b0f92f5abc-upower.service-M5NB7T
tracker-extract-3-files.1000
vmware-user
vmware-root
Распечатать размер файлов заданного расширения (у нас *.txt) в MB, в текущей дирректории:
$ find . -type f -iname '*.txt' -exec ls -lG {} \; | awk '{total = total + $4}END{print "scale=2;" total "/2^20"}' | bc
10.68
Посмотреть тип файловой системы
$ DIR=. ; FSTYPE=$(df -TP ${DIR} | grep -v Type | awk '{ print $2 }') ; echo "${FSTYPE}"
ext4
Погода в терминале
curl http://wttr.in, если локализация не правильная, указать непосредственно: curl http://wttr.in/moscow
НО, нам интересен только заголовок поэтому:
$ curl http://wttr.in | sed '1,7! d' > wheather; clear; date >> wheather; cat wheather
Weather report: St Petersburg, Russia
\ / Partly cloudy
_ /"".-. -13(-20) °C
\_( ). ← 11 km/h
/(___(__) 10 km
0.0 mm
Fri Dec 8 07:51:46 PM MSK 2023
Функция:
$ forecast () { curl http://wttr.in | sed '1,7! d' > wheather; clear; date >> wheather; cat wheather; }
Приветствие в открывающемся терминале
$ echo -e "12 morning\n15 afternoon\n24 evening" | awk '{ if ('`date +%H`'<$1) { print "Good "$2 " comrade";exit } }'
Good evening comrade
или так:
$ echo -e '12 morning! Today: '`date +%A`'\n15 afternoon Time:' "\033[1m`date +%T`\033[0m"'\n24 evening! Now:' "\033[1m`date +%T`\033[0m"'' | awk '{ if ('`date +%H`'<$1) { print "Good "$2, $3, $4"";exit } }'
Good evening! Now: 15:25:31
PS: добавить в профильный файл, например .bashrc (не всякая "реализация" интерпритатора будет читать .profile, а .bashrc, прочтет точно) конечно при желании можно раскрасить, выбрать шрифт и прочее ...(/home/Doc/reference-book-of-shell )
Найти устройство по точке монтирования
$ df -P | awk '$6=="/Data" {print $1}'
/dev/sda10
Удалить все процессы найденные посредствам grep
$ for line in `ps aux | grep [string] | awk '{print $2}'`; do sudo kill $line; done;
Убить процесс найденный посредствам "ps"
$ ps ux|grep <имя процесса>|awk '{print $2}'|xargs -n 1 kill
Распечатать время последнего изменения в формате «дата — файл»
$ ls -alt /home/$USER | awk '{ print $6 " " $7 " -- " $9 }' | column -c 140
-- Dec 10 -- tmpgrade.txt Oct 3 -- .mozilla
Dec 26 -- . Dec 10 -- echo Sep 28 -- .selected_editor
Dec 26 -- .bash_history Dec 9 -- .vmware Sep 26 -- .gnome
Dec 26 -- .xsession-errors Dec 9 -- .ssh Sep 26 -- .pki
Dec 26 -- .lesshst Dec 8 -- exec Sep 26 -- sudo
Dec 26 -- .Xauthority Dec 8 -- curl Sep 26 -- .sudo_as_admin_successful
Dec 25 -- speedtest Dec 8 -- wttr.in Sep 26 -- .gtk-bookmarks
Dec 25 -- wheather Dec 8 -- .cache Sep 26 -- music
Dec 21 -- .config Dec 7 -- sed Sep 26 -- tmp
Dec 20 -- .viminfo Dec 6 -- ls Sep 26 -- backup
Dec 20 -- images Dec 2 -- conkyrc Sep 26 -- bin
Dec 19 -- foonction Dec 2 -- .flags Sep 26 -- .fonts.conf
.....................................................................
Утилита: column
Распечатать десятку самых жирных файлов используя du, в нашем случае из $HOME
$ for i in `du --max-depth=1 $HOME | sort -n -r | awk '{print $1 ":" $2}'`; do size=`echo $i | awk -F: '{print $1}'`; dir=`echo $i | awk -F: '{print $NF}'`; size2=$(($size/1024)); echo "$size2 MB used by $dir"; done | head -n 10
11951 MB used by /home/user
5432 MB used by /home/user/.local
2377 MB used by /home/user/videos
1365 MB used by /home/user/.config
1290 MB used by /home/user/.cache
925 MB used by /home/user/documents
407 MB used by /home/user/downloads
61 MB used by /home/user/.cr3
34 MB used by /home/user/vmware-host-modules
26 MB used by /home/user/.mozilla
Посмотреть загруженные модули ядра
$ sudo lsmod | cut -d' ' -f1 | xargs modinfo | egrep '^file|^desc|^dep' | sed -e'/^dep/s/$/\n/g'
modinfo: ERROR: Module Module not found.
filename: /lib/modules/6.1.0-12-amd64/kernel/drivers/misc/vmnet.ko
description: VMware Virtual Networking Driver.
depends:
filename: /lib/modules/6.1.0-12-amd64/kernel/net/vmw_vsock/vmw_vsock_vmci_transport.ko
description: VMCI transport for Virtual Sockets
depends: vsock,vmw_vmci
filename: /lib/modules/6.1.0-12-amd64/kernel/net/vmw_vsock/vsock.ko
description: VMware Virtual Socket Family
depends:
.........................................................
Отображение заголовка отчета
Результат работы предыдущей команды выглядит не слишком привлекательно. Рассмотрим, какие шаги можно предпринять, чтобы улучшить его. Прежде всего выровняем границы полей посредством символов табуляции. Табуляция создается с помощью Escape–последовательности \t (об управляющих последовательностях речь пойдет ниже). Кроме того, для придания отчету солидности добавим к нему заголовок, включающий названия полей, а также разделительную линию, которая отображается в отдельной строке благодаря Escape–последовательности \n.: Заголовок отчета формируется в процедурной части шаблона begin.
$ ~ awk 'BEGIN {print "Name Belt\n --------------"} \
{print $1 " \t" $4}' grade.txt
Name Belt
---------------------------
Tansley Green
Lulu green
Bunny Yellow
Troll Brown-3
Tansley Brown-2
$ ~
Более практический пример, вывести 10 наиболее загруженных процессов:
$ ps axo rss,comm,pid | awk '{ proc_list[$2]++; proc_list[$2 "," 1] += $1; } END { for (proc in proc_list) { printf("%d\t%s\n", proc_list[proc "," 1],proc); }}' | sort -n | tail -n 10 | sort -rn
686MB telegram-desktop
246MB gnome-shell
128MB systemd-journal
127MB mariadbd
118MB Xorg
103MB dockerd
84MB apache2
76MB evolution-alarm
58MB containerd
Усугубим "изврашение" при помощи BEGIN и END:
$ ps axo rss,comm,pid | awk '{ proc_list[$2]++; proc_list[$2 "," 1] += $1; } END { for (proc in proc_list) { printf("%d\t%s\n", proc_list[proc "," 1],proc); }}' | sort -n | tail -n 10 | sort -rn | awk '{$1/=1024;printf "%.0fMB\t",$1}{print $2}' | awk 'BEGIN {print "\n======= TOP TEN ======="} {print $1" \t" $2} END {print "========= END =========="}'
======= TOP TEN =======
4101MB chromium
686MB telegram-desktop
246MB gnome-shell
128MB systemd-journal
127MB mariadbd
118MB Xorg
103MB dockerd
84MB apache2
76MB evolution-alarm
58MB containerd
========= END ==========
Десять основных процессов с процентами от общего объема ОЗУ:
$ TR=`free|grep Mem:|awk '{print $2}'`;ps axo rss,comm,pid|awk -v tr=$TR '{proc_list[$2]+=$1;} END {for (proc in proc_list) {proc_pct=(proc_list[proc]/tr)*100; printf("%d\t%-16s\t%0.2f%\n",proc_list[proc],proc,proc_pct);}}'|sort -n |tail -n 10
24224 tracker-miner-f 0.62%
28324 systemd-journal 0.73%
33920 qbittorrent 0.87%
37252 gjs 0.96%
38280 fwupd 0.98%
43328 packagekitd 1.11%
53548 Xorg 1.37%
63004 gnome-software 1.62%
213412 gnome-shell 5.48%
2683192 chromium 68.89%
Процессы по количеству открытых файловых дескрипторов
$ lsof | awk '{print $1}' | sort | uniq -c | sort -rn | head
49786 chromium
7222 gnome-shell
4138 evolution
4032 gjs
3653 qbittorrent
2322 gnome-soft
1595 conky
1400 Xorg
1146 xdg-desktop
1102 gnome-session
$ history | awk '{a[$2]++}END{for(i in a){print a[i] " " i}}' | sort -rn | head | awk 'BEGIN {print "\n"}{print $1" \t-" $2}'
183 -sudo
100 -ls
84 -systemctl
60 -for
50 -history
47 -ps
40 -netstat
34 -vim
31 -cd
28 -echo
PS: оптимальным будет настроить под себя ReadLine:
https://wiki.archlinux.org/title/Readline https://www.opennet.ru/docs/RUS
При работе со строками используем SED
Удалить строку, интервал между строками используя sed:
Удалить третью строку: sed '3d' file
Удалите строку, содержащую ряд букв: sed '/awk/d' file
Удалить последнюю строку: sed '$d' file
Удалить интервал между строками 7 и 9: sed '7,9d' file
PID выбранного окна GUI:
$ xprop | awk '/PID/ {print $3}'
2950
Cписок процессов по прослушиваемому порту:
$ sudo netstat -ntlp | grep -w 80 | awk '{print $7}' | cut -d/ -f1
972
$ sudo netstat -ntlp | grep -w 54560 | awk '{print $7}' | cut -d/ -f1
17226
$ lsof -p 17226 | awk '{print $1}'
COMMAND
qbittorrent
qbittorrent
.........
Функция pid процесса, по его имени:
$ pidof () { ps acx | egrep -i $@ | awk '{print $1}'; }
$ pidof chromium
4459
4559
4560
4571
4619
......
$ pidof Xorg
1811
Текстовой график количества подключений
$ netstat -an | grep ESTABLISHED | awk '{print $5}' | awk -F: '{print $1}' | sort | uniq -c | awk '{ printf("%s\t%s\t",$2,$1) ; for (i = 0; i < $1; i++) {printf("*")}; print "" }'
108.177.14.139 1 *
108.177.14.188 1 *
151.101.194.137 1 *
173.194.220.94 1 *
192.168.1.1 1 *
209.85.233.94 2 **
2.19.198.50 1 *
5.255.255.77 1 *
64.233.164.95 1 *
87.250.251.89 2 **
Все IP соединения хоста:
$ sudo netstat -lantp | grep ESTABLISHED |awk '{print $5}' | awk -F: '{print $1}' | sort -u
172.67.217.229
193.106.95.138
5.255.255.77
52.85.49.39
64.233.163.94
74.125.205.188
77.88.21.119
87.240.132.67
87.250.250.121
87.250.251.89
Отображение резюме отчета
Чтобы добавить в конец отчета строку "end‑of‑report", следует воспользоваться шаблоном END. Этот шаблон употребляется для обозначения действий, которые выполняются после обработки последней записи входного файла.
$ ~ awk 'BEGIN {print "Name\n------------ "} {print $1} \
END {print "end‑of‑report"}' grade.txt
Name
------------
M.Tansley
N.Lulu
O.Bunny
P.Troll
Q.Tansley
end-of-report
$ ~
Следует обратить внимание на обратный слеш используемый awk как символ перевода строки (возврат каретки), в отличии от bash,sh написание будет просто слеш, а не так например:
$ ~ echo -e "строка 1\nстрока 2"
строка 1
строка 2
$ ~
И если точнее, то выше приведенный сценарий awk состоит как бы из "двух частей"
awk 'BEGIN {print "Name\n------------ "} {print $1} \
END {print "end‑of‑report"}' grade.txt
между которыми будет напечатано приглашение в виде (1:) или (>) в зависимости как вы его указали в профильных файлах, в качестве переменной PS2=... ,которое не выводится, и если, по частям и если PS2=> (echo $PS2) то примерно так:
$ ~ awk 'BEGIN {print "Name\n------------ "} {print $1} \
> END {print "end‑of‑report"}' grade.txt
Name
------------
M.Tansley
N.Lulu
O.Bunny
P.Troll
Q.Tansley
end‑of‑report
$ ~
PS: Использование шаблонов BEGIN и END, это далеко не только "красота отчетов" (естественно). Во многих ситуациях может потребоваться выполнить код инициализации до того, как awk начнет обработку текста из входного файла (потока), как раз для таких ситуаций awk позволяет использовать шаблон BEGIN, блок BEGIN оценивается до того, как awk начнет обработку входного файла, отличное место для инициализации переменной FS (разделитель полей), вывода заголовка или инициализации других глобальных переменных, на которые вы будете ссылаться позже.
Как и шаблон END, Awk выполняет этот шаблон после обработки всех строк входного файла. Как правило, блок END используется для выполнения окончательных вычислений или печати сводок, которые должны появиться в конце выходного потока, примером может послужить скрипт который слева и использование первого END в третьей строке, если выполнять его по строчно добавляя > {print $1,$2}' , то можно без труда разобраться во всех его "замысловатостях".
$ netstat -ntu | awk '{print $5}'|cut -d: -f1 -s|sort|uniq -c|sort -nk1 -r | awk 'BEGIN {print "\n=== NETSTAT-TCP/UDP Connection ==="} {print $1" \t" $2} END {print "========== netstat -ntu ==========="}'
=== NETSTAT-TCP/UDP Connection ===
6 62.217.160.2
2 62.217.160.4
2 62.217.160.3
1 8.8.8.8
1 77.88.55.88
1 74.125.131.95
1 62.217.160.12
1 173.194.73.199
========== netstat -ntu ===========
Количество подключений на удаленные адреса;
$ netstat -antu | awk '{print $5}' | awk -F: '{print $1}' | sort | uniq -c | sort -n
1 151.101.130.137
1 192.168.1.1
1 74.125.205.188
1 Address
1 and
11
18 0.0.0.0
Список установленных соединений по "назначению" и идентификатору приложения.
$ netstat | grep EST | awk '{print $5}' | sort
192.168.1.1:bootps # bootstrap protocol
e2a.google.com:https
lh-in-f102.1e100.:https
lh-in-f95.1e100.n:https
li-in-f199.1e100.:https
li-in-f199.1e100.:https
lm-in-f132.1e100.:https
lo-in-f84.1e100.n:https
lr-in-f94.1e100.n:https
lu-in-f188.1e100.n:5228 #порт 5228
List the number and type of active network connections
# netstat -ant | awk '{print $NF}' | grep -v '[a-z]' | sort | uniq -c
2 CLOSE_WAIT
20 ESTABLISHED
7 LISTEN
# netstat -antp | grep ::80*
tcp6 0 0 :::80 :::* LISTEN 1023/apache2
# NETSTAT Статистика сетевых соединений ss Man ss
$ lsof -Pni4 # | grep LISTEN (check open ports)
-P no port names
-n no host names
-i select IPv[4or6]
Вывести имена всех сетевых интерфейсов и IPv4-адресов:
$ PS: ifconfig часто бывает излишне "богатая" утилита
$ alias ips='ip a | awk '\''/inet /&&!/ lo/{print $NF,$2}'\'' | column -t'
$ ips
wlan0 192.168.1.201/24
vmnet1 172.16.193.1/24
vmnet8 172.16.11.1/24
$ ip -f inet a | awk '/inet / { print $2 }'
127.0.0.1/8
192.168.1.201/24
172.16.193.1/24
172.16.11.1/24
$ MAC адреса интерфейсов:
$ for f in /sys/class/net/*; do echo -e "$(basename $f)\t$(cat $f/address)"; done
eth0 d4:be:d9:5a:b0:3b
lo 00:00:00:00:00:00
vmnet1 00:50:56:c0:00:01
vmnet8 00:50:56:c0:00:08
wlan0 68:5d:43:eb:05:0a
$ cat /sys/class/net/*/address
d4:be:d9:5a:b0:3b
00:00:00:00:00:00
00:50:56:c0:00:01
00:50:56:c0:00:08
68:5d:43:eb:05:0a
Локализация установленных соединений:
$ sudo apt install geoip-bin geoip-database
$ geoiplookup 8.8.8.8
GeoIP Country Edition: US, United States
$ for i in $(netstat --inet -n|grep ESTA|awk '{print $5}'|cut -d: -f1);do geoiplookup $i;done
GeoIP Country Edition: RU, Russian Federation
GeoIP Country Edition: RU, Russian Federation
GeoIP Country Edition: NL, Netherlands
GeoIP Country Edition: US, United States
GeoIP Country Edition: US, United States
GeoIP Country Edition: US, United States
Вывести имя текущего соединения:
$ nmcli -g name,type connection show --active|awk -F: '/ethernet|wireless/ { print $1 }'
ROSTELECOM
PS: Для беспроводных соединения существует удобная утилита iw, если добавить AWK, будут получаться интересные отчеты
Все порты прослушивания, плюс PID и FD
$ lsof -Pan -i tcp -i udp
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
chrome 3044 164u IPv4 188458 0t0 UDP 224.0.0.251:5353
chrome 3044 169u IPv4 188460 0t0 UDP 224.0.0.251:5353
chrome 3044 253u IPv4 188462 0t0 UDP 224.0.0.251:5353
................................
Обработка сообщений об ошибках
При работе с утилитой awk почти невозможно избежать синтаксических ошибок. Эта утилита выводит на экран искомую строку и указывает, в каком ее месте возникла ошибка. Но сами сообщения об ошибках не слишком информативны и не всегда могут помочь в разрешении проблем. Давайте смоделируем ситуацию, при которой возникает синтаксическая ошибка, например, пропустим двойную кавычку в предыдущей команде:
$ ~ awk 'BEGIN {print "Name\n---------------- "} {print $1} \
> END {print "\end-of-report}' grade.txt
awk: cmd. line:2: END {print "\end-of-report}
awk: cmd. line:2: ^ unterminated string
awk: cmd. line:2: END {print "\end-of-report}
awk: cmd. line:2: ^ syntax error
$ ~
Впервые сталкиваясь с утилитой awk, краткость подобных сообщений безусловно смущает. Предлагаем вам перечень правил обнаружения ошибок:
убедитесь, что сценарий awk целиком заключен в одинарные кавычки;
удостоверьтесь, что все кавычки внутри сценария являются парными;
проверьте, заключены ли процедуры в фигурные скобки, а условные конструкции - в круглые скобки.
Более понятным является сообщение об ошибке, возникающей при создании ссылки на несуществующий файл:
$ ~ awk 'END {print "End‑of‑report"}' grades.txt
awk: fatal: cannot open file `grades.txt' for reading (Нет такого файла или каталога)
$ ~
Ввод данных с клавиатуры
Давайте посмотрим, что произойдет, если не указать файл grade.txt в командной строке:
$ ~ awk 'BEGIN {print "Name Belt\n----------------------- "} \
> {print $1" \t"$4}'
Name Belt
-----------------------
>
^C
$ ~
С помощью шаблона BEGIN на экран выводится заголовок отчета, при этом сам отчет пуст, а утилита awk ожидает получения входных данных с клавиатуры (об этом свидетельствует строка приглашения >). Вы должны ввести их вручную. После нажатия клавиши [Enter] введенная строка интерпретируется как входная запись и по отношению к ней выполняются соответствующие инструкции. По завершении ввода данных нажмите [Ctrl+D]. Подобный метод работы применяется довольно редко, поскольку чреват большим количеством опечаток и ошибок.
$ awk 'BEGIN {print "Name Belt\n----- "} {print $1" \t"$4}'
Name Belt
-----
^C
$
Метасимволы
Если коротко о регулярных выражениях, неизбежном атрибуте любого языка, то: в сценарии awk регулярное выражение выделяется с обеих сторон символами косой черты: /регулярное_выражение/. Например, если в текстовом файле нужно найти строку, содержащую слово "Green", следует задать шаблон /Green/. Перечисленные метасимволы могут встречаться в регулярных выражениях утилиты awk: \ ^ $ . [ ] | ( ) * + ?
Следует остановиться на описании двух метасимволов, которые специфичны для awk и не применяются в команде grep и редакторе sed.
+ Указывает на то, что предыдущий символ встречается один или несколько раз. Например:
выражение /t+/ соответствует одной или нескольким буквам 't', а
выражение /[а–z]+/ — любой последовательности строчных букв.
? Указывает на то, что предыдущий символ встречается не более одного раза. Например:
выражение /xy?z/ соответствует строкам "XYZ" и "XZ".
Стоит так-же обратить внимание на использование прямого и обратного слеша, в зависимости от цели.
Операторы
В awk существует достаточно много операторов, манипулирующих числами, строками, переменными, полями и элементами массива.
Список основных операторов:
Операторы сравнения
Простейшие инструкции awk создаются с помощью операторов:
Проверка на совпадение
Оператор ~ (тильда) позволяет находить поля, соответствующие заданному шаблону (регулярному выражению). Обычно он применяется в конструкции if, условная часть которой заключается в круглые скобки.
Предположим, из файла grade.txt требуется извлечь информацию о владельцах коричневых поясов. Для этого нужно найти строки, содержащие слово "Brown" (коричневый):
$ ~ awk '{if($4 ~ /Brown/) print $0}' grade.txt
P.Troll 07/99 4842 Brown-3 12 26 26
Q.Tansley 05/99 4712 Brown-2 12 30 28
$ ~
Конструкция if является частью сценария awk и помещается в фигурные скобки. Поставленную задачу можно решить намного проще, если вспомнить, что при нахождении строки, соответствующей шаблонной части инструкции, утилита awk по умолчанию отображает всю строку. Таким образом, можно вообще не указывать команду print, а условную часть конструкции if представить в виде шаблона:
$ ~ awk '$0 ~ /Brown/' grade.txt
P.Troll 07/99 4842 Brown-3 12 26 26
Q.Tansley 05/99 4712 Brown-2 12 30 28
$ ~
Приведенная команда означает следующее: если в строке встречается слово "Brown", вывести ее на экран.
Проверка на равенство
Допустим, необходимо получить информацию об ученике с номером 48. Показанная ниже команда не позволит решить данную задачу, поскольку в файле есть множество номеров, содержащих последовательность цифр "48":
$ ~ awk '{if($3 ~ /48/) print $0}' grade.txt
M.Tansley 05/99 48311 Green 8 40 44
N.Lulu 06/99 48317 green 9 24 26
O.Bunny 02/99 48 Yellow 12 35 28
P.Troll 07/99 4842 Brown-3 12 26 26
$ ~
Чтобы найти точное совпадение, воспользуйтесь оператором ==:
$ ~ awk '$3 == "48"' grade.txt
O.Bunny 02/99 48 Yellow 12 35 28
$ ~
Проверки на несовпадение и неравенство
Иногда требуется извлечь те строки, которые не соответствуют шаблону. Для этих целей предназначен оператор !~, выполняющий проверку на неравенство регулярному выражению. Давайте, например, выведем список всех учеников, не являющихся обладателями коричневого пояса:
$ ~ awk '$0 !~ /Brown/' grade.txt
M.Tansley 05/99 48311 Green 8 40 44
N.Lulu 06/99 48317 green 9 24 26
O.Bunny 02/99 48 Yellow 12 35 28
$ ~
He забывайте, что по умолчанию утилита awk выводит на экран все записи, отвечающие указанному критерию отбора, поэтому нет необходимости задавать какое‑либо действие. Если вместо предыдущей команды указать
$ ~ awk '$4 != "Brown"' grade.txt
M.Tansley 05/99 48311 Green 8 40 44
N.Lulu 06/99 48317 green 9 24 26
O.Bunny 02/99 48 Yellow 12 35 28
P.Troll 07/99 4842 Brown-3 12 26 26
Q.Tansley 05/99 4712 Brown-2 12 30 28
$ ~
получим ошибочный результат. Эта команда означает, что требуется найти строки, в которых четвертое поле не равно "Brown". Такому критерию удовлетворяют все строки в файле. Конечно, если нужно найти обладателей поясов, отличных от "Brown-2", можно применить следующую команду:
$ ~ awk '$4 != "Brown-2"' grade.txt
M.Tansley 05/99 48311 Green 8 40 44
N.Lulu 06/99 48317 green 9 24 26
O.Bunny 02/99 48 Yellow 12 35 28
P.Troll 07/99 4842 Brown-3 12 26 26
$ ~
Обратите внимание на один важный момент: строка "Brown-2" заключена в двойные кавычки. Если этого не сделать, четвертое поле будет сравниваться с содержимым переменной Brown-2. Вряд ли в вашем интерпретаторе существует такая переменная, поэтому вместо нее будет подставлена пустая строга, и вы получите совершенно другой результат.
Проверка "меньше чем"
Допустим, нужно определить, кто из учеников не смог набрать максимального количества очков на соревновании. Для выполнения проверки достаточно сравнить набранный рейтинг (поле 6) с общей суммой возможных очков (поле 7). Также поместим в отчет небольшое сообщение.
$ ~ awk '{if($6 < $7) print $1 " — try better at the next competition"}' grade.txt
M.Tansley — try better at the next competition
N.Lulu — try better at the next competition
$ ~
Проверка "меньше или равно"
Чтобы включить в отчет тех учеников, рейтинг которых не выше значения в седьмом поле, нужно лишь незначительно видоизменить предыдущий пример:
$ ~ awk '{if($6 <= $7) print $1}' grade.txt
M.Tansley
N.Lulu
P.Troll
$ ~
Проверка "больше чем"
Следующая команда формирует список лидеров соревнования:
$ ~ awk '{if ($6 > $7) print $1}' grade.txt
O.Bunny
Q.Tansley
$ ~
Логические операторы
Логические операторы или булевы (boolean), обладающие двумя значениями true и false, позволяют формировать сложные выражения, позволяющие выполнять проверку нескольких условий. Существует три логических оператора:
Оператор логического И &&
Предположим, перед нами поставлена задача выяснить, кому был присвоен зеленый пояс в мае 1999 года. Строки, отвечающие этому условию, должны в первом поле содержать значение "05/99", а во втором — "Green":
$ ~ awk '{if($2 == "05/99" && $4 == "Green") print $0}' grade.txt
M.Tansley 05/99 48311 Green 8 40 44
$ ~
Оператор логического ИЛИ ||
Чтобы получить список учеников, обладающих желтым или коричневым поясом, воспользуйтесь оператором | |:
$ ~ awk '{if($4 == "Yellow" || $4 ~ /Brown/) print $0}' grade.txt
O.Bunny 02/99 48 Yellow 12 35 28
P.Troll 07/99 4842 Brown-3 12 26 26
Q.Tansley 05/99 4712 Brown-2 12 30 28
$ ~
Приведенную команду можно упростить с помощью метасимвола, означающего выбор любого из двух шаблонов:
$ ~ awk '$4 ~ /Yellow|Brown/' grade.txt
O.Bunny 02/99 48 Yellow 12 35 28
P.Troll 07/99 4842 Brown-3 12 26 26
Q.Tansley 05/99 4712 Brown-2 12 30 28
$ ~
Операторы присваивания и арифметические операторы
С помощью операторов присваивания и арифметических операторов можно создавать в сценариях awk локальные переменные и манипулировать их значениями.
Создание локальных переменных
При разработке сценариев awk не всегда удобно работать с идентификаторами полей. Лучше создать переменную, содержащую значение поля, и присвоить ей выразительное имя, чтобы в дальнейшем ссылаться на поле по имени. Подобную переменную можно получить с помощью конструкции следующего вида: имя_переменной = $n где n -cуществующий номер поля.
В следующем примере мы создадим две переменные: name, содержащую имена учеников (поле 1), и belts, содержащую названия поясов (поле 4). Затем будет произведен поиск учеников, обладающих желтым поясом.
$ ~ awk '{name=$1; belts=$4; if(belts ~ /Yellow/) print name " is belt " belts}' grade.txt
O.Bunny is belt Yellow
$ ~
Обратите внимание на то, что команды сценария отделяются друг от друга точкой с запятой.
Проверка значения поля
В следующем примере мы проверим, кто из учеников набрал в соревнованиях менее 27 очков. В первом варианте команды значение поля $6 непосредственно сравнивается с числом 27:
$ ~ awk '$6 < 27' grade.txt
N.Lulu 06/99 48317 green 9 24 26
P.Troll 07/99 4842 Brown-3 12 26 26
$ ~
Более универсальный способ заключается в том, чтобы перед обработкой входного файла, в процедурной части шаблона BEGIN, создать, как в настоящей программе, набор локальных переменных с нужными значениями и ссылаться на них, когда потребуется. Конечно, этот прием неэффективен в случае одноразовых команд, зато очень полезен в больших сценариях, так как позволяет легко вносить в них изменения. Во втором примере мы создадим переменную baseline и присвоим ей значение 27, а затем сравним с ней интересующее нас поле $6:
$ ~ awk 'BEGIN {BASELINE=27} {if ($6 < BASELINE) print $0}' grade.txt
N.Lulu 06/99 48317 green 9 24 26
P.Troll 07/99 4842 Brown-3 12 26 26
$ ~
Изменение значения числового поля
Изменяя значение поля, следует помнить о том, что содержимое входного файла на самом деле не меняется. Изменению подвергается только копия файла, которая хранится в буфере awk.
В следующем примере на экран выводятся имена учеников и их рейтинговые очки, а рейтинг ученика по имени M. Tansley уменьшается на единицу:
$ ~ awk '{if($1=="M.Tansley") $6=$6-1; print $1, $6}' grade.txt
M.Tansley 39
N.Lulu 24
O.Bunny 35
P.Troll 26
Q.Tansley 30
$ ~
Изменение значения текстового поля
Для изменения значения текстового поля достаточно применить к нему оператор присваивания. Следующая команда при выводе на экран добавляет к имени ученика J. Troll дополнительный инициал:
$ ~ awk '{if($1 == "P.Troll") $1="J.L.Troll"; print $1}' grade.txt
M.Tansley
N.Lulu
O.Bunny
J.L.Troll
Q.Tansley
$ ~
He забывайте о том, что строковые константы следует заключать в двойные кавычки. Поскольку имена учеников в данном случае содержат точки, утилита awk выдаст сообщение об ошибке при отсутствии кавычек, так как точка является метасимволом и встречается в непонятном контексте.
Исключить строку с помощью AWK
$ awk '{sub("String","")}1'
Например:
$ awk '{sub("N.Lulu 06/99 48317 green 9 24 26","")}1' grade.txt
M.Tansley 05/99 48311 Green 8 40 44
O.Bunny 02/99 48 Yellow 12 35 28
P.Troll 07/99 4842 Brown-3 12 26 26
Q.Tansley 05/99 4712 Brown-2 12 30 28
# разделитель <Space>
Отображение только измененных записей
При работе с файлами большого объема часто нет необходимости отображать все записи, а достаточно вывести лишь те из них, которые подверглись изменениям. По отношению к предыдущему примеру это означает, что все команды после конструкции if следует дополнительно заключить в фигурные скобки:
$ ~ awk '{if($1 == "P.Troll") {$1="J.L.Troll"; print $1}}' grade.txt
J.L.Troll
$ ~
Исключить несколько столбцов с помощью AWK
$ awk '{$1=$3="";print}' grade.txt #равно awk '{$1=$3=""}1'
05/99 Green 8 40 44
06/99 green 9 24 26
02/99 Yellow 12 35 28
07/99 Brown-3 12 26 26
05/99 Brown-2 12 30 28
$ awk '{$2=$3=$5=$6=$7="";}1' grade.txt
M.Tansley Green
N.Lulu green
O.Bunny Yellow
P.Troll Brown-3
Q.Tansley Brown-2
Awk рассматривает "1" как истину и поэтому выполняет действие по умолчанию, действие по умолчанию печать.
Создание нового поля
Аналогично локальным переменным в сценарии awk можно создавать новые поля. Например, мы можем создать поле $8, содержащее разницу полей $6 и $7 в том случае, если значение в поле $7 больше, чем в поле $6:
$ ~ awk 'BEGIN {print "Name\t\tDifference"} {if($6 < $7) {$8=$7-$6; print $1" \t"$8}}' grade.txt
Name Difference
M.Tansley 4
N.Lulu 2
$ ~
Суммирование столбцов
Для вычисления суммарного рейтинга учеников секции мы создадим переменную tot и с помощью выражения tot+=$6 будем прибавлять к ней значение поля $6 при обработке каждой записи. По завершении обработки записей в процедурной части шаблона END итоговое значение переменной tot будет выведено на экран.
$ ~ awk 'tot+=$6; END {print "Club student total points: " tot}' grade.txt
M.Tansley 05/99 48311 Green 8 40 44
N.Lulu 06/99 48317 green 9 24 26
O.Bunny 02/99 48 Yellow 12 35 28
P.Troll 07/99 4842 Brown-3 12 26 26
Q.Tansley 05/99 4712 Brown-2 12 30 28
Club student total points: 155
$ ~
Вероятно, вы заметили, что утилите awk не было дано указание выводить на экран все записи oна сделала это сама. Причина такого поведения заключается в том, что выражение tot+=$6 относится к шаблонной части инструкции и не задает критерия отбора строк, т.е. применяется ко всем записям. А поскольку процедурная часть этого шаблона отсутствует, выполняется действие по умолчанию — команда print SO.
Если файл велик, можно не выводить на экран все записи, а лишь отобразить итог. Для этого достаточно взять выражение tot+=$6 в фигурные скобки, чтобы перенести его в процедурную часть инструкции:
$ ~ awk '{tot+=$6}; END {print "Club student total points: " tot}' grade.txt
Club student total points: 155
$ ~
Счетчик пользовательских процессов:
$ ps aux |awk '{$1} {++P[$1]} END {for(a in P) if (a !="USER") print a,P[a]}'
message+ 1
rtkit 1
colord 1
dell 97
polkitd 1
www-data 6
root 164
$ ps hax -o user --sort user | uniq -c
1 colord
98 dell
1 messagebus
1 polkitd
164 root
1 rtkit
6 www-data
Найти все файлы размером более 10 МБ, отсортировать в порядке убывания размера:
# find . -size +10240k -exec ls -l {} \; | awk '{ print $5,"",$9 }'|sort -rn
548382816 ./VMware-Workstation-Full-16.0.0/VMware-Workstation-Full-16.2.3-19376536.x86_64.bundle
538839908 ./VMware/VMware-Workstation-Full-17.0.0-20800274.x86_64.bundle
536072059 ./VMware/VMware_15.5/VMware-Workstation-Full-15.5.2-15785246.x86_64.bundle
527417929 ./VMware/VMware-Workstation-Full-16.1.2-17966106.x86_64.bundle
Показать разделы, которые используются более чем на 70%
# df -h |awk '{a=$5;gsub(/%/,"",a);if(a > 70){print $0}}'
Filesystem Size Used Avail Use% Mounted on
/dev/sda7 38G 34G 2.2G 94% /
/dev/sda6 124G 109G 9.3G 93% /Data
Показать количество сетевых карт, портов на каждую сетевую карту и адрес PCI:
$ lspci | grep Ether | awk '{ VAR=$1; split(VAR,ARR,"."); count[ARR[1]]++; LINE=$0; split(LINE,LINEARR,":"); LINECOUNT[ARR[1]]=LINEARR[3]; } END { for(i in count) { printf("PCI address: %s\nPorts: %d\nCard Type: %s\n", i, count[i], LINECOUNT[i]) } }'
PCI address: 07:00
Ports: 1
Card Type: Realtek Semiconductor Co., Ltd. RTL810xE PCI Express Fast Ethernet controller (rev 05)
Суммирование размеров файлов
При просмотре содержимого каталога часто требуется узнать общий размер всех файлов в нем, исключая файлы в подкаталогах и скрытые файлы. Алгоритм решения этой задачи таков: результаты работы команды ls -l (формирует список файлов с расширенной информацией о них) направляются утилите awk, которая удаляет записи, начинающиеся с символа 'd' (признак каталога), и вычисляет сумму по 5–му столбцу (содержит размер файла).
Представленная ниже команда отображает список файлов текущего каталога (имя файла берется из 9–го столбца), указывая размер каждого из них, а в конце выводит суммарный размер файлов, накопленный в переменной tot:
$ ~ ls -l | awk '/^[^d]/ {print $9"\t"$5; tot+=$5} END {print "total KB: "tot}'
LogAnalyser.png 242620
big 702
butt.jpg* 26558
date 69987
dvb-fe-xc5000-1.6.114.fw 12401
dvb-usb-dib0700-1.20.fw 33768
find.txt 7055732
grade.txt 176
grep.txt 0
................................
total KB: 15956555
$ ~
Если необходимо включить в список скрытые файлы, следует вместо команды
ls -l задать команду ls -la.
Количество символов в каждой строке:
$ awk ' BEGIN { FS=""} { print NF } ' myfile
7
7
7
4
3
7
4
3
7
$ cat myfile
line -1
line -2
line -3
vxbh
jjj
ubdjuys
vxbh
jjj
ubdjuys
$
Использование памяти отдельной программой:
$ ps auxf | grep chromium | grep -v grep | grep -v defunct | awk '{sum=sum+$6}; END {print sum/1024}'
2869.57
$ ps auxf | grep apache2 | grep -v grep | grep -v defunct | awk '{sum=sum+$6}; END {print sum/1024}'
14.2852
$ ps auxf | grep gnome-shell | grep -v grep | grep -v defunct | awk '{sum=sum+$6}; END {print sum/1024}'
228.57
Общий процент использования памяти для процессов с заданным именем:
$ ps -eo pmem,comm | grep chromium | awk '{sum+=$1} END {print sum " % of RAM"}'
67.5 % of RAM
$ ps -eo pmem,comm | grep apache2 | awk '{sum+=$1} END {print sum " % of RAM"}'
1.6 % of RAM
$ ps -eo pmem,comm | grep gnome-shell | awk '{sum+=$1} END {print sum " % of RAM"}'
7 % of RAM
Текущее использование памяти, включая использование SWAP, всеми активными процессами:
$ ps aux | awk '{sum+=$6} END {print sum/1024}'
3436.94
$ ps aux | awk '$11!~/\[*\]/ {print $6/1024" Mb --> "$11,$12}' | sort -g
Встроенные переменные
Утилита awk имеет ряд встроенных переменных, которые позволяют получить подробную информацию о входном потоке и настройках awk. Значения некоторых переменных можно изменять. https://www.gnu.org/software/gawk/manual
Переменная ARGC хранит число аргументов командной строки, переданной сценарию awk (точнее, nawk или gawk, т. к. эта переменная появилась только в новых версиях утилиты).
Переменная ARGV хранит значения аргументов командной строки. Доступ к нужному аргументу осуществляется с помощью ссылки ARGV[n], где п — порядковый номер аргумента в командной строке.
Переменная ENVIRON хранит значения всех текущих переменных среды. Чтобы получить доступ к нужной переменной, укажите ее имя, например: ENVIRON["EDITOR"]=="vi"
Поскольку сценарий awk может обрабатывать большое количество файлов, предусмотрена переменная FILENAME, которая указывает на то, какой файл просматривается в текущий момент.
Переменная FNR хранит номер записи, которую утилита awk обрабатывает в текущий момент; ее меньше или равно значению переменной NR, которая отслеживает общее число обработанных записей входного потока. Если сценарий получает доступ более чем к одному файлу, переменная FNR сбрасывается в ноль при открытии каждого нового файла.
В переменную NF - Number of Fields записывается количество полей текущей записи. Ее значение сбрасывается по достижении конца записи.
Переменная FS - Field Srparator содержит символ, используемый в качестве разделителя полей входного потока. Эту переменную можно установить из командной строки с помощью опиии -F. по умолчанию разделителем полей служит пробел.
Переменная OFS содержит символ, являющийся разделителем полей в выходном потоке. По умолчанию это тоже пробел.
В переменной ORS хранится разделитель записей в выходном потоке. По умолчанию им является символ новой строки (\n).
Переменная RS содержит разделитель записей во входном потоке (в большинстве случаев это тоже символ \n).
Переменные NF, NR и FILENAME
Представленная ниже команда позволяет быстро определить число записей(строк) во входном файле grade.txt. Значение переменной NR отображается по завершении обработки файла.
$ ~ awk 'END {print NR}' grade.txt
5
Текущий интерфейс:
$ route | grep -m1 ^default | awk '{print $NF}'
wlan0
В следующем примере на экран выводятся все записи исходного файла. Каждой из них предшествуют два числа: количество полей в записи (переменная NF) и номер записи в файле (переменная NR). В самом конце отображается имя входного файла (переменная FILENAME).
$ ~ awk '{print NF, NR, $0} END {print FILENAME}' grade.txt
7 1 M.Tansley 05/99 48311 Green 8 40 44
7 2 N.Lulu 06/99 48317 green 9 24 26
7 3 O.Bunny 02/99 48 Yellow 12 35 28
7 4 P.Troll 07/99 4842 Brown-3 12 26 26
7 5 Q.Tansley 05/99 4712 Brown-2 12 30 28
grade.txt
$ ~
Распечатать номера строк; awk '{print NR,$0}' например:
$ cat grade.txt | awk '{print NR,$0}'
1 M.Tansley:_05/99:_48311:_Green:_8:_40:_44
2 N.Lulu:_06/99:_48317:_green:_9:_24:_26
3 O.Bunny:_02/99:_48:_Yellow:_12:_35:_28
4 P.Troll:_07/99:_4842:_Brown-3:_12:_26:_26
5 Q.Tansley:_05/99:_4712:_Brown-2:_12:_30:_28
6 #:_разделитель:_<Space>
Переменную NF удобно использовать, когда требуется извлечь из путевого имени последнюю часть, т. е. имя файла или каталога. В этом случае необходимо указать, что разделителем полей является символ '/' и использовать ссылку $NF, которая является обозначением последнего поля текущей записи. Например:
$ ~ pwd
/home/iarch
$ ~ echo $PWD | awk -F/ '{print $NF}'
iarch
$ ~
#Переменная среды $PWD хранит путевое имя текущего каталога.
OFMT Формат распечатки чисел
Встроенная переменная OFMT содержит спецификацию формата по умолчанию, которую print использует с sprintf при превращении числа в цепочку символов для печати. Это значение OFMT равно "%.6g".
OFMT позволяет использовать различные значения в качестве спецификаций формата, ...можно менять форму чисел, печатаемых оператором print.
$ awk 'BEGIN {
> OFMT = "%.0f" #будет округлять
> print 17.23, 17.54 }'
17 18
$ awk 'BEGIN { OFMT = "%.0f"; print 17.23, 17.54 }'
17 18
Функция калькулятора оболочки с плавающей запятой, то есть операции над числами с "двойной точностью" числами вещественными или действительными тире "самыми обыкновенными":
$ calc() { awk 'BEGIN { OFMT="%f"; print "равно: "'"$*"'; exit}'; }
$ calc 2.4567 + 3.2345
равно: 5.691200
$
PS: Можно конечно поставить утилиту:
$ sudo apt-get install calc
$ calc 2.4567 + 3.2345
5.6912
$ ...но с функцией интересней, и глубину разряда можно регулировать.
Для математики лучше пользоваться Lua с ее библиотекой "math", Lua устанавливают наверное по умолчанию "везде":
$ lua -e "print (math.sin(12))"
-0.53657291800043
$ lua -e "print (math.cos(12))"
0.84385395873249
$ lua -e "print (2.4567 + 3.2345)"
5.6912
$ Lua
> = 1 + 2 + 4
7
> = 21 / 45
0.46666666666667
> ... великолепный калькулятор
Функция конвертации байтов в удобочитаемый размер файла:
$ human_filesize() { awk -v sum="$1" ' BEGIN {hum[1024^3]="Gb"; hum[1024^2]="Mb"; hum[1024]="Kb"; for (x=1024^3; x>=1024; x/=1024) { if (sum>=x) { printf "%.2f %s\n",sum/x,hum[x]; break; } } if (sum<1024) print "1kb"; } ';}
$ human_filesize 1234567890
1.15 Gb
$ ...а можно пользоваться и утилитой numfmt
$ numfmt --to=iec-i <<< "1234567890"
1.2Gi
$ numfmt --to=iec-i <<< "12345678907654323456"
11Ei
$ human_filesize 12345678907654323456
11497809465.65 Gb
Извлечение строк
$ awk 'NR == 2' grade.txt
N.Lulu 06/99 48317 green 9 24 26
$ awk 'NR == 3' grade.txt
O.Bunny 02/99 48 Yellow 12 35 28
Комбинировать строки с полями:
$ awk 'NR == 3{print$1}' grade.txt
O.Bunny
$ awk 'NR == 3{print$2}' grade.txt
02/99
$ awk 'NR == 3{print$3}' grade.txt
48
$ awk 'NR == 3{print$4}' grade.txt
Yellow
$ awk 'NR == 3{print$5}' grade.txt
12
$ awk 'NR == 3{print$1 $4}' grade.txt
O.BunnyYellow
Подставить в команду:
Например есть некий процесс:
$ ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=109 time=20.2 ms
64 bytes from 8.8.8.8: icmp_seq=2 ttl=109 time=17.2 ms
......................................................
$ ps aux | grep [p]ing #[p]ing - убрать из вывода grep "grep"
dell 2546 0.0 0.0 309192 7740 ? .....
dell 3122 3.7 10.8 3720148 868588 ? .....
dell 16659 0.0 0.0 7136 736 pts/0 ..ping 8.8.8.8
Узнаем его pid:
$ ps aux | grep [p]ing | awk 'NR == 3{print$2}'
16659
$
Выполнить, применив подстановку:
$ ps aux | grep [p]ing | kill -9 $(awk 'NR == 3{print$2}')
В итоге:
$
............................................................
64 bytes from 8.8.8.8: icmp_seq=128 ttl=109 time=18.2 ms
64 bytes from 8.8.8.8: icmp_seq=129 ttl=109 time=19.2 ms
Killed ... terminated by signal SIGKILL (Forced quit)
$
С использованием xargs:
$ ps aux | grep [p]ing | awk 'NR == 3{print$2}' | xargs -n 1 kill
$ Terminated
PS: Есть и "штатный" способ исключить из вывода "grep", это ключ -v самой утилиты:
$ ps aux | grep ping | grep -v grep
..........................................
$ ps aux | grep ping | grep -v grep | awk 'NR == 3{print$2}' | xargs -n 1 kill
$ Terminated
Проще не бывает:
$ ps -ef | grep [p]ing | awk '{print $2}' | xargs kill
$ Terminated
Для тех кому не нравиться AWK и Xargs. существует великолепная команды pkill, команда убивающая процесс по "патерну", синтаксис таков: pkill -f my_pattern например:
$ ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
64 bytes from 8.8.8.8: icmp_seq=1 ttl=104 time=34.6 ms
$ ps aux | grep [p]ing
40160 0.0 0.0 7540 1116 pts/3 S+ 13:09 0:00 ping 8.8.8.8
$ pkill -f ping
$......................................
64 bytes from 8.8.8.8: icmp_seq=121 ttl=104 time=33.4 ms
64 bytes from 8.8.8.8: icmp_seq=122 ttl=104 time=33.5 ms
Terminated
Lua - Стандартные арифметические операции:
"+" - сложение,
"-" - вычитание,
"/" - деление,
"*" - умножение,
"%" - остаток от деления (5%2 == 1).
Встроенные функции работы со строками
GNU AWK / Built-in functions 9 Functions /gawk/manual
Как и любой язык, а начинали мы с того что awk, какой ни какой но все же язык и как в любом языке в нем предусмотрены функции, смысл и назначение такое же как и везде, то есть это некий блок кода с заданной функциональностью, только хранятся видимо они не в оперативной память, как в Bash, после загрузки, а там .... где то в глубинах awk. Функции awk это инструментарий выработанный "практикой использования" утилиты. В результате утилита awk располагает набором универсальных функций преобразования строк. Ниже перечислены основные из них. Функции работы со строками:
Функция gsub()
Благодаря функции gsub() вы сможете выполнить в текущей записи глобальную замену строк, соответствующих заданному регулярному выражению. Например, для изменения номера ученика с 4842 на 4899 введите такую команду:
$ ~ awk 'gsub(4842,4899) {print $0}' grade.txt
P.Troll 07/99 4899 Brown-3 12 26 26
$ ~
Функция index()
Чтобы узнать позицию первого вхождения подстроки t в строку s, воспользуйтесь функцией index (), только не забудьте взять ее аргументы в двойные кавычки, иначе они будут восприниматься как имена переменных среды. Например, следующая команда возвращает число, определяющее позицию подстроки "ny" в строке "Bunny":
$ ~ awk 'BEGIN {print index("Bunny","ny")}' grade.txt
4
$ ~
Функция length()
Функция length() возвращает длину переданного ей текстового аргумента. В показанном ниже примере производится поиск информации об ученике с номером 4842, а затем определяется длина имени ученика:
$ ~ awk '$3==4842 {print length($1)" "$1}' grade.txt
7 P.Troll
$ ~
Следующая команда демонстрирует применение утилиты awk для вычисления длины текстовой строки:
$ ~ awk 'BEGIN {print length("A FEW GOOD MEN")}'
14
$ ~
Функция match()
Функция match() позволяет проверить, содержит ли строка заданную подстроку. Последняя может быть представлена как литералом в двойных кавычках, так и регулярным выражением. Если поиск прошел успешно, возвращается число, определяющее позицию, с которой начинается вхождение подстроки в искомую строку. В случае неудачи возвращается ноль. Следующая команда проверяет, содержит ли имя ученика с номером 48317 символ 'u':
$ ~ awk '$3==48317 {print match ($1, "u"), $1} ' grade.txt
4 N.Lulu
$ ~
Функция split()
Функция split() преобразует переданную ей строку в массив и возвращает число элементов в полученном массиве. В следующем примере заданная строка разбивается на три элемента, которые помещаются в массив myarray. Разделителем элементов в данном случае является символ '#'.
$ ~ awk 'BEGIN {print split("123#456#678", myarray,"#")}'
3
$ ~
Массив myarray будет иметь такую структуру:
mуarray[1]="123"
myarray[2]="456"
myarray[3]="678"
Функция sub()
Функция sub() применяется для поиска строки, соответствующей заданному шаблону, и ее замены при первом появлении. В этом состоит отличие данной функции от функции gsub(), которая находит все случаи вхождения подстроки в строку, производя соответствующее число замен. Приведенная ниже команда находит запись ученика P. Troll и меняет его рейтинг с 26 на 29 (поле 6), при этом значение поля 7 (тоже 26) остается неизменным:
$ ~ awk '$1=="P.Troll" {sub(26,29,$0)} {print $0}' grade.txt
M.Tansley 05/99 48311 Green 8 40 44
N.Lulu 06/99 48317 green 9 24 26
O.Bunny 02/99 48 Yellow 12 35 28
P.Troll 07/99 4842 Brown-3 12 29 26
Q.Tansley 05/99 4712 Brown-2 12 30 28
Исключить строку:
$ awk '{sub("O.Bunny","")}1' grade.txt
M.Tansley 05/99 48311 Green 8 40 44
N.Lulu 06/99 48317 green 9 24 26
02/99 48 Yellow 12 35 28
P.Troll 07/99 4842 Brown-3 12 26 26O
Q.Tansley 05/99 4712 Brown-2 12 30 28
Вернуть:
$ awk '$1=="O.Bunny" {print substr($1,1,7)}1' grade.txt
M.Tansley 05/99 48311 Green 8 40 44
N.Lulu 06/99 48317 green 9 24 26
O.Bunny
O.Bunny 02/99 48 Yellow 12 35 28
P.Troll 07/99 4842 Brown-3 12 26 26O
Q.Tansley 05/99 4712 Brown-2 12 30 28
Зачем нужен символ "1" в конце инструкции?: awk оценивает "1" как истину и по умолчанию печатает всю строку, включая новую строку.
$ echo 'foo' | awk '/foo/{print}1'
foo
foo
Функция substr()
Функция substr() возвращает указанную часть строки. Вам нужно задать позицию, с которой начинается вхождение подстроки в искомую строку, и длину подстроки. Рассмотрим пример:
$ ~ awk '$1=="M.Tansley" {print substr($1,1,5)}' grade.txt
M.Tan
$ ~
Эта команда возвращает из строки "M. Tansley" подстроку, начинающуюся с первого символа и занимающую пять символов.
Если значение третьего аргумента значительно превышает реальную длину строки, функция substr () возвращает все символы строки, начиная с указанной позиции:
$ ~ awk '$1=="M.Tansley" {print substr ($1,3, 99)} ' grade.txt
Tansley
$ ~
То же самое происходит, когда третий аргумент вообще не указан. Например, следующая команда формирует список фамилий учеников:
$ ~ awk '{print substr($1,3)}' grade.txt
Tansley
Lulu
Bunny
Troll
Tansley
$ ~
Передача строк из интерпретатора shell утилите awk
Очень часто утилита awk получает входные данные не из файла, а по каналу (pipe"|") от других команд. Рассмотрим несколько примеров обработки строк, поступающих из канала. В первом примере команда echo передает строку "Stand‑by" утилите awk, которая вычисляет ее длину:
$ ~ echo "Stand‑by" | awk '{print length($0)}'
8
$ ~
Во втором примере утилита awk получает строку с именем файла и возвращает имя файла без расширения:
$ ~ echo "mydoc.txt" | awk '{print substr($STR,1,5)}'
mydoc
$ ~
Следующая команда возвращает только расширение файла:
$ ~ echo "mydoc.txt" | awk '{print substr($STR,7)}'
txt
$ ~
Escape–последовательности
При работе со строками и регулярными выражениями нередки случаи включения в шаблон поиска непечатаемых символов (таких как символ новой строки либо табуляции) или же символов со специальным значением в утилите awk (любой из метасимволов). Такие символы создаются с помощью управляющих Escape–последовательностей, признаком которых является обратный слеш в начале. Например, шаблон поиска открывающей фигурной скобки выглядит так: /\|/
Escape–последовательности утилиты awk:
В следующей команде сначала отображается фраза "May Day", в которой слова разделены символом табуляции, а затем выводятся два символа новой строки, вследствие чего образуется пустая строка. Потом отображается слово "May", а за ним cлово "Day", каждая буква которого представлена ASCII–кодом: 'D' - 104, 'а'- 141,'у'- 171.
$ ~ awk 'BEGIN {print "May\tDay\n\nMay \104\141\171"}'
May Day
May Day
$ ~
Команда printf
Во всех примерах, с которыми мы ознакомились, данные выводились на экран с помощью команды print без какого‑либо форматирования. В awk имеется намного более мощная команда printf, аналог одноименной функции языка С, позволяющая задавать правила форматирования выходных данных. В Linux это встроенная команда оболочки, аналог echo, с более замысловатым синтаксисом и более широкими возможностями (aidalinux.ru/w/Printf):
$ type -a printf
printf is a shell builtin
printf is /usr/bin/printf
$
Базовый синтаксис команды таков: printf "строка_формaтирования", аргументы
Строка форматирования может включать как литеральные символы, записываемые в выходной поток без изменения, так и спецификаторы форматирования, каждый из которых задает способ интерпретации соответствующего ему аргумента.
Спецификаторы форматирования:
В состав спецификаторов форматирования могут входить различные модификаторы, определяющие дополнительные особенности форматирования. Модификатор помещается между символом '%' и управляющим символом.
Дополнительные модификаторы:
Преобразование символов
Чтобы узнать, ASCII–код какого символа равен 65, можно с помощью команды echo направить строку "65" утилите awk и затем передать ее в качестве аргумента команде printf со спецификатором %с. Команда printf выполнит преобразование автоматически:
$ ~ echo "65" | awk '{printf "%c\n", $0}'
A
$ ~
Как видите, это символ 'A'. Обратите внимание на наличие в команде символа новой строки (\n). Необходимость в нем объясняется тем, что команда printf по умолчанию не записывает этот символ в выходной поток. Если не указать Escape–последовательность \n, сразу после буквы 'А' в той же строке будет отображено приглашение интерпретатора shell, что может смутить пользователя, работающего в данный момент за терминалом. Конечно, код символа может быть указан непосредственно в команде printf:
$ ~ awk 'BEGIN {printf "%c\n", 65}'
A
$ ~ awk 'BEGIN {printf "%c\n", 66}'
B
$ ~ awk 'BEGIN {printf "%c\n", 67}'
C
$ ~
От десятичного:
$ awk 'BEGIN {printf "%c\n", 33}'
!
$ awk 'BEGIN {printf "%c\n", 36}'
$
$ awk 'BEGIN {printf "%c\n", 37}'
%
От "hex"
$ awk 'BEGIN {print "\x21"}'
!
$ awk 'BEGIN {print "\x24"}'
$
$ awk 'BEGIN {print "\x25"}'
%
От восьмиричного:
$ awk 'BEGIN {print "\041"}'
!
$ awk 'BEGIN {print "\044"}'
$
$ awk 'BEGIN {print "\045"}'
%
Обратно, воспользовавшись ord:
$ echo "!" | awk 'BEGIN{for(n=0;n<256;n++)ord[sprintf("%c",n)]=n}{print ord[$1]}'
33
$ echo "$" | awk 'BEGIN{for(n=0;n<256;n++)ord[sprintf("%c",n)]=n}{print ord[$1]}'
36
$ echo "%" | awk 'BEGIN{for(n=0;n<256;n++)ord[sprintf("%c",n)]=n}{print ord[$1]}'
37
Используя printf (форматированный вывод), аналог одноименной функции языка С в Linux это встроенная команда оболочки:
$ type -a printf
printf is a shell builtin
printf is /usr/bin/printf
$
$ printf "%d\n" "'!"
33 # десятичное
$ printf "%o\n" "'!"
41 # восьмиричное
$ printf "%x\n" "'!"
21 # "hex"
Форматированный вывод
Предположим, необходимо отобразить имена учеников и их идентификаторы, причем имена должны быть выровнены по левому краю и размещаться в поле длиной 15 символов. В конце строки форматирования стоит символ новой строки (\n), служащий для разделения записей. Выводимые данные будут размещаться в двух колонках:
$ ~ awk '{printf "%-15s %d\n", $1, $3}' grade.txt
M.Tansley 48311
N.Lulu 48317
O.Bunny 48
P.Troll 4842
Q.Tansley 4712
$ ~
Передача переменных утилите awk
Переменные можно создавать не только в сценарии awk, но и непосредственно в командной строке. Формат вызова утилиты awk в этом случае таков:
awk 'сценарий' переменная=значение входной_файл
В следующем примере переменная AGE создается в командной строке и инициализируется значением 10. Показанная команда находит студентов, возраст которых не превышает 10 лет.
$ ~ awk '{if($5 < AGE) print $0}' AGE=10 grade.txt
M.Tansley 05/99 48311 Green 8 40 44
N.Lulu 06/99 48317 green 9 24 26
$ ~
Рассмотрим более сложный пример. Системная команда df отображает информацию о смонтированных файловых системах. Опция [- к] команды df устанавливает размер блока равным 1 Кб (что наиболее привычно). Результаты ее работы по умолчанию имеют следующий формат:
# df -k
Файловая система 1K-блоков Использовано Доступно Использовано% Cмонтировано в
dev 505636 0 505636 0% /dev
run 509256 736 508520 1% /run
/dev/sda1 20511356 11614800 7831596 60% /
tmpfs 509256 4 509252 1% /dev/shm
tmpfs 509256 0 509256 0% /sys/fs/cgroup
tmpfs 509256 36 509220 1% /tmp
/dev/sda2 38057472 30873968 5227256 86% /home
/dev/sda3 19091584 13087876 5010788 73% /mnt/Debian
/dev/sda4 17964924 10753644 6275652 64% /mnt/Kali
tmpfs 101848 0 101848 0% /run/user/0
tmpfs 101848 20 101828 1% /run/user/1000
#
# cd /
# du -h --max-depth=1
286G ./media
8,0K ./mnt
77G ./Data
..........
# cd /var
# du -h --max-depth=1
156M ./cache
2,0M ./NX
...............
Чтобы узнать, в какой из имеющихся файловых систем объем свободного пространства ниже критической отметки, следует передать выходные данные команды df утилите awk и последовательно сравнить значения в четвертом столбце с пороговым значением. В следующей командной строке пороговое число задано в виде переменной TRIGGER, которая равна 5000000 - 5G:
# df -k | awk '$4 ~ /^[0-9]/ {if($4 < TRIGGER) print $1"\t"$4}' TRIGGER=5000000
dev 505636
run 508520
tmpfs 509252
tmpfs 509256
tmpfs 509220
tmpfs 101848
tmpfs 101828
$ ~
Проверка $4 ~ /^[0-9]/ позволяет отсечь заголовок отчета команды df, содержащий в четвертом столбце строку "Доступно".
Разделы использующие более 50%(или сколько надо) дискового пространства:
$ df -h |awk '{a=$5;gsub(/%/,"",a);if(a > 50){print $0}}'
Filesystem Size Used Avail Use% Mounted on
/dev/sda10 77G 61G 12G 85% /Data
/dev/sda5 124G 106G 12G 90% /Share
Вот еще один пример. Команда who выводит сведения о пользователях, зарегистрировавшихся в системе. В первом столбце выходных данных этой команды отображаются регистрационные имена пользователей, а во втором — имена терминалов, к которым подключены эти пользователи. С помощью утилиты awk вы можете отфильтровать результаты работы команды who и узнать к какому терминалу подключены: ,
$ ~ who
iarch tty1 2017-05-07 13:49
iarch pts/0 2017-05-08 15:20 (192.168.1.100)
$ ~ who | awk '{if($1==user) print $1 " you are connected to " $2}' user=$LOGNAME
iarch you are connected to tty1
iarch you are connected to pts/0
$ ~
Здесь локальная переменная user инициализируется значением переменой среды $LOGNAME, которая хранит регистрационное имя текущего пользователя.
Файлы сценариев
Если последовательность команд awk слишком велика, чтобы вводить ее в командной строке, или предназначена для многократного использования, целесообразно поместить ее в отдельный файл сценария. Преимуществом сценариев является также возможность добавления комментариев, благодаря которым вы сможете быстро вспомнить назначение той или иной программы. Давайте возьмем один из рассмотренных ранее примеров и преобразуем его в файл сценария awk. В частности, следующая, уже знакомая нам, команда подсчитывает суммарный рейтинг учеников секции:
$ ~ awk 'tot+=$6; END {print "Club student total points; " tot}' grade.txt
M.Tansley 05/99 48311 Green 8 40 44
N.Lulu 06/99 48317 green 9 24 26
O.Bunny 02/99 48 Yellow 12 35 28
P.Troll 07/99 4842 Brown-3 12 26 26
Q.Tansley 05/99 4712 Brown-2 12 30 28
Club student total points; 155
$ ~
Создадим на ее основе файл, который назовем student_tot.awk. Расширение awk является общепринятым соглашением относительно именования файлов сценариев awk. Вот текст этого файла:
$ ~ cat > student_tot.awk
#!/bin/awk -f #Чаще #!/usr/bin/awk -f: $ type awk: awk is /usr/bin/awk
#Все строки комментариев должны начинаться с символа '#'
#Имя файла: student_tot.awk
#Командная строка: student_tot.awk grade.txt
#Вычисление суммарного и среднего рейтинга учеников секции.
#Сначала выводим заголовок.
BEGIN {
print "Student Date Member Grade Age Points Max"
print "Name joined Gained point available"
print "============================================="
}
# Суммируем рейтинг учеников.
(tot+=$6)
# В завершение выводим суммарный и средний рейтинг.
END {
print "Общий балл студенческого клуба:" tot
print "Средние баллы студенческого клуба:" tot/NR
}
^C
$ ~
В этом сценарии мы формируем заголовок отчета и вычисляем не только суммарный рейтинг, но и средний рейтинг учеников секции, который получается путем деления переменной tot на переменную NR, содержащую число записей во входном файле. Как видите, на языке awk можно писать полноценные программы, содержащие команды, комментарии, а также пустые строки и дополнительные пробелы, предназначенные для повышения удобочитаемости текста сценария.
Ключевым моментом сценария является первая строка, выглядящая как комментарий: #!/bin/awk -f
Это своеобразная системная инструкция с названием shebang, указывающая, какая программа должна выполнять данный сценарий. Подобная инструкция должна быть первой строкой любого сценария. Шебанг (shebang, sha-bang, hashbang, pound-bang, or hash-pling) — в программировании последовательность из двух символов: решётки и восклицательного знака ("#!") в начале файла скрипта. Общий ее формат таков: #!/путь/программа [командная_строка]
Когда происходит запуск исполняемого файла, система проверяет, начинается ли он с "магической" последовательности. Если нет, значит, файл содержит машинные коды и выполняется непосредственно. Если же обнаружено выражение #!, то это файл сценария. В таком случае происходит следующее:
Первая строка сценария заменяет собой командную строку, из нее удаляется "магическая" последовательность;
Предыдущая командная строка передается новой командной строке в качестве аргумента.
В нашем случае это означает, что при запуске сценария вместо команды
$ student_tot.awk grade.txt
в действительности выполняется такая команда:
$ /bin/awk -f student_tot.awk grade.txt
Опция -f утилиты awk говорит о том, что выполняемые команды находятся в указанном вслед за ней файле.
После создания файл student_tot.awk необходимо сделать его исполняемым с помощью команды
$ chmod +x student_tot.awk или chmod 744 student_tot.awk
Ну и все остальное,....стоит добавить, что есть более "магическая" запись, это ./ - точка слэш, позволяющая выполнение исполняемого файла из любой дирректории, не обращая внимание на переменую $PATH.
И вот результаты работы сценария:
$ ~ ./student_tot.awk grade.txt
Student Date Member Grade Age Points Max
Name joined Gained point available
=============================================
M.Tansley 05/99 48311 Green 8 40 44
N.Lulu 06/99 48317 green 9 24 26
O.Bunny 02/99 48 Yellow 12 35 28
P.Troll 07/99 4842 Brown-3 12 26 26
Q.Tansley 05/99 4712 Brown-2 12 30 28
Общий балл студенческого клуба:155
Средние баллы студенческого клуба:31
$ ~
Использование переменной FS в сценариях awk
Если просматривается файл, в котором разделителем полей является символ, отличный от пробела, например '#' или ':', это легко учесть, указав в командной строке опцию ' -F:'
$ awk -F: '{print $0}' входной_файл
Аналогичную функцию выполняет переменная FS, которую можно установить непосредственно в сценарии. Следующий сценарий обрабатывает файл /etc/passwd, выводя на экран содержимое первого и пятого полей, включающих соответственно имя пользователя и описание роли пользователя в системе. Поля в этом файле разделены символом двоеточия, поэтому в процедурной части шаблона begin устанавливается переменная FS, значение которой заключается в двойные кавычки:
$ ~ cat > passwd.awk
#!/usr/bin/awk -f
# Имя файла: student_tot.awk
# Командная строка: passwd.awk /etc/passwd
# Вывод содержимого первого и пятого полей файла passwd.
BEGIN {
FS=":"
}
{print $1"\t"$5}
^C
$ ~
$ ~ chmod 744 passwd.awk (chmod +x passwd.awk)
$ ~
Вот возможные результаты работы этого сценария:
$ ~ ./passwd.awk /etc/passwd
root root
bin bin
daemon daemon
mail mail
ftp ftp
http http
uuidd uuidd
dbus dbus
nobody nobody
................
...............
$ ~
Передача переменных сценариям awk
При передаче переменной сценарию awk формат командной строки таков:
awk файл_сценария переменная=значение входной_файл
Следующий небольшой сценарий сравнивает количество полей в каждой записи входного файла с заданным в командной строке значением. Текущее количество полей хранится в переменной NF. Сравниваемое значение передается сценарию в виде переменной МАХ.
$ cat > fieldcheck.awk
#!/usr/bin/awk -f
#Имя файла: fieldcheck.awk
#Командная строка: fieldcheck.awk MAX=n [FS=<разделитель>] имя_файла
#Проверка числа полей в записях файла.
NF!=MAX{
print("line " NR " does not have " MAX " fields")}
$ ~ chmod +x fieldcheck.awk
Файл /etc/passwd, содержит семь полей с разделителем ":", указав другое значение получим ("line " NR " does not have " MAX " fields"):
$ ./fieldcheck.awk MAX=7 FS=":" /etc/passwd
$
$ ./fieldcheck.awk MAX=6 FS=":" /etc/passwd
line 1 does not have 6 fields
line 2 does not have 6 fields
line 3 does not have 6 fields
........................
line 61 does not have 6 fields
line 62 does not have 6 fields
$
Следующий сценарий выводит информацию об учениках, чей возраст ниже значения, заданного в командной строке:
cat > age.awk
#!/usr/bin/awk -f
# Имя файла: age.awk
# Командная строка: age.awk AGE=n grade.txt
# Вывод информации об учениках, чей возраст ниже заданного,
{if($5 < AGE) print $0}
^C
$ ~ chmod +x age.awk
$ ~ ./age.awk AGE=10 grade.txt
M.Tansley 05/99 48311 Green 8 40 44
N.Lulu 06/99 48317 green 9 24 26
$ ~
Массивы
Изучая функцию split(), мы говорили о том, каким образом ее можно использовать для преобразования строки символов в массив. Повторим соответствующий пример:
$ ~ awk 'BEGIN {print split("123#456#678", myarray, "#")}'
3
$ ~
Функция split() возвращает число элементов созданного массива myarray. Внутренняя структура массива myarray в этом случае такова:
myarray[1]="123"
myаrray[2]="456"
myarray[3]="678"
При работе с массивами в awk не требуется заранее их объявлять. Не нужно также указывать количество элементов массива. Для доступа к массиву обычно применяется цикл for, синтаксис которого следующий: for (элемент in массив) print массив[элемент]
В показанном ниже сценарии функция split() разбивает заданную строку на элементы и записывает их в массив myarray, а в цикле for содержимое массива выводится на экран:
$ ~ cat > arraytest.awk
#!/usr/bin/awk -f
#Имя файла: arraytest.awk
#Командная строка: arraytest.awk /dev/null
#Вывод элементов массива.
BEGIN {
record="l23#456#789";
split(record, myarray, "#") }
END {
for (i in myarray) print myarray[i]
}
^C
$ ~ chmod 744 arraytest.awk
$ ~ ./arraytest.awk /dev/null
l23
456
789
$ ~
Для запуска сценария в качестве входного файла следует указать устройство /dev/null. Если этого не сделать, утилита awk будет ожидать поступления данных с клавиатуры.
Статические массивы
В предыдущем примере массив формировался динамически. Следующий пример является более сложным и демонстрирует, как создавать в сценарии статические массивы. Ниже показан входной файл grade_student.txt, включающий информацию об учениках нашей секции каратистов. Записи файла содержат два поля: первое — название пояса, которым владеет ученик, второе — категория ученика (взрослый, юниор). Разделителем полей служит символ '#'.
$ ~ cat > grade_student.txt
Vellow#Junior
Orange#Senior
Yellow#Junior
Purple#Junior
Brown-2#Junior
White#Senior
Drange#Senior
Red#Junior
Brown-2#Senior
Yellow#Senior
Red#Junior
Blue#Senior
Green#Senior
Purple#Junior
White#Junior
^C
$ ~
Наша задача заключается в том, чтобы прочитать файл и получить следующие сведения:
1.Сколько учеников имеют желтый, оранжевый и красный пояса?
2.Сколько взрослых и детей посещают секцию?
Рассмотрим такой сценарий:
$ ~ cat >belts.awk
#!/usr/bin/awk -f
#Имя файла: belts.awk
#Командная строка: belts.awk grade_student.txt
#Подсчет числа учеников, имеющих желтый, оранжевый и красный пояса,
#а также количества взрослых и юных членов секции.
# Задание разделителя и создание массивов.
BEGIN {FS="#"
# Создание массива, индексами которого являются названия поясов
belt["Yellow"]
belt["Orange"]
belt["Red"]
#Создание массива, индексами которого являются названия категорий учеников
student["Junior"]
student["Senior"]
}
#Если значение первого поля совпадает с индексом массива belt,
#содержимое соответствующего элемента массива увеличивается на единицу
{for(colour in belt)
{if ($1==colour)
belt[colour]++}}
# Если значение второго поля совпадает с индексом массива student
# содержимое соответствующего элемента массива увеличивается на единицу
{for (senior_or_junior in student)
{if ($2==senior_or_junior)
student [senior_or_junior] ++}}
# Вывод полученных результатов
END {for (colour in belt)
print "The club has ", belt[colour],colour,"Belts"
print "*******************************"
for (senior_or_junior in student) print "The club has",\
student[senior_or_junior],senior_or_junior,"students"
}
^C
$ ~
$ ~ chmod +x belts.awk
$ ~
В процедурной части шаблона begin в переменную FS записывается символ '#" - pазделитель полей во входном файле. Затем создаются два массива, belt и student, индексами которых являются соответственно названия поясов и названия категорий учеников. Оба массива остаются не инициа-лизированными, их тип не определен.
В первом цикле for значение первого поля каждой записи входного файла сравнивается с названием индекса массива belt (индекс равен "Yellow", "Orange" или "Red"). Если обнаруживается совпадение, выполняется приращение элемента, хранящегося в массиве по этому индексу. Поскольку операция ++ (инкремент) определена для целочисленных значений, утилита awk считает, что массив целочисленный, и инициализирует его элементы значением 0.
Во втором цикле for значение второго поля сравнивается с названием индекса массива student (индекс равен "Junior" или "Senior"). Результат операции вычисляется так же, как и в первом цикле.
В процедурной части шаблона END осуществляется вывод итоговых результатов. Итого:
$ ~ ./belts.awk grade_student.txt
The club has 2 Red Belts
The club has 1 Orange Belts
The club has 2 Yellow Belts
**************************
The club has 7 Senior students
The club has 7 Junior students
$ ~
Язык программирования awk может показаться сложным для изучения, но если осваивать его на примерах употребления отдельных команд и небольших сценариев, то процесс обучения не будет слишком трудным. Утилита awk является важным инструментом shell–программирования, и чтобы применять ее, вам не обязательно быть экспертом по языку awk.
![](https://www.google.com/images/icons/product/drive-32.png)
![](https://www.google.com/images/icons/product/drive-32.png)
![](https://www.google.com/images/icons/product/drive-32.png)
![](https://www.google.com/images/icons/product/drive-32.png)
![](https://www.google.com/images/icons/product/drive-32.png)
![](https://www.google.com/images/icons/product/drive-32.png)
SED
Работа с редактором sed
Команды sed вводятся в командной строке либо размещаются в файле сценария подобно тому, как это делается в случае с утилитой awk. При использовании sed важно помнить следующее: этот редактор оставляет без изменения исходный файл независимо от того, какая команда выполняется. Копия входного потока помещается в буфер редактирования, а все изменения направляются на экран или переадресуются в выходной файл. Поскольку редактор sed не предназначен для работы в интер-активном режиме, текстовые строки, подлежащие изменению, отбираются либо по номерам, либо на основании соответствия регулярному выражению.
Чтение и обработка данных в sed
Общая схема работы редактора sed такова:
Редактор последовательно извлекает строки текста из файла или стандартного входного потока и копирует их в буфер редактирования.
Затем он считывает первую команду из командной строки или сценария, осуществляет поиск строки с указанным номером или строки, соответствующей шаблону, и применяет к ней эту команду.
Второй пункт повторяется до тех пор, пока не будет исчерпан список команд.
Вызов редактора sed
Вызвать редактор sed можно тремя способами:
Ввести набор команд sed в командной строке.
Поместить набор команд sed в файл и передать его редактору sed в командной строке.
Поместить набор команд sed в файл сценария и сделать его выполняемым.
Если редактор sed вызывается для выполнения одиночных команд, формат командной строки будет таким: sed [опции] 'команды' входной_файл
Как и в awk, не забудьте заключить команды в одинарные кавычки, чтобы защитить от интерпрета-ции находящиеся в них специальные символы.
Если команды sed помещены в отдельный файл, командная строка примет следующий вид: sed [опции] — f файл_сценария входной_файл
Если файл сценария является исполняемым, запустить его на выполнение следует таким образом: файл_сценария [опции] входной_файл
Когда входной файл не указан, sed будет ожидать поступления данных из стандартного входного потока: с клавиатуры или из канала.
Ниже перечислены основные опции редактора sed и описано их назначение:
-n Запрет вывода на экран. При наличии этой опции редактор sed не будет записывать обрабатываемые им строки в стандартный выходной поток, тогда как по умолчанию отображается каждая входная строка. Осуществить вывод нужной строки можно будет только с помощью команды p (рассматривается ниже).
-e Следующей командой будет команда редактирования. Эта опция используется в том случае, когда команд редактирования несколько. Если же имеется только одна такая команда, то указывать данную опцию не нужно, хотя ее наличие и не является ошибкой.
-f Эта опция используется при подключении файла сценария.
Сохранение выходных данных
Если требуется сохранить проделанные изменения, просто перенаправьте результаты работы редактора sed в файл стандартным оператором перенаправления вывода, например так : $ sed 'команды' входной_файл > выходной_файл
Синтаксис команд
Общий синтаксис команд редактора sed таков:
[адрес1 [, адрес2] ] [ ! ] команда [аргументы]
Команда состоит из одной буквы или одного символа (Список основных команд представлен ниже). Аргументы требуются лишь нескольким командам, в частности, команде s. Элементы, представлен-ные в квадратных скобках, являются необязательными, а сами скобки набирать не нужно.
Просмотр входного файла по умолчанию начинается с первой строки. Существует два способа адресации строк:
По номерам
С помощью регулярных выражений (не отъемлимом атрибуте любой сложной команды)
В команде может быть указано два адреса, один адрес или ни одного адреса. В следующей таблице описаны все возможные правила отбора строк в зависимости от того, сколько компонентов адреса задано. Некоторые команды, в частности, a, i, r, q и =, требуют указания только одного адреса.
Правила отбора строк в редакторе sed:
Основные команды редактирования
Ниже представлен список основных команд, имеющихся в редакторе sed. Основные команды sed:
Вы можете использовать несколько скриптов в одной команде, для этого перед каждым скриптом используйте ключ -e. С помощью фигурных скобок можно объединить несколько команд в группу. Возможны два синтаксиса группировки:
[адрес1[, адрес2]]{
команда 1
....................
команда n
}
или
[адрес1[,адрес2]] {команда1; …командаN; }
В первом случае каждая команда записывается в отдельной строке, а разделителем команд является символ новой строки. Во втором случае команды записываются последовательно, отделяясь друг от друга точкой с запятой, которая ставится также после завершающей команды. Ниже приведен текстовый файл quote.txt, который будет использоваться во многих примерах ниже:
$ ~ cat > quote.txt
The honeysuckle band played all night long for only $90.
It was an evening of splendid music and company.
Too bad the disco floor fell through at 23:10.
The local nurse Miss P. Neave was in attendance.
^C
$ ~
Регулярные выражения
Редактор sed распознает базовые регулярные выражения. Дополнительные особенности появляются только в шаблонах поиска и замены в команде s. С помощью операторов \ ( и \) можно сохранить до девяти шаблонов поиска во временном буфере, с тем чтобы в шаблоне замены обратиться к ним с помощью оператора \n, где n — номер сохраненного шаблона. Метасимвол & позволяет в шаблоне замены сослаться на фрагмент строки, соответствующий шаблону поиска.
Вывод строк (команда p)
Рассмотрим, как в редакторе sed осуществляется поиск строк и вывод их на экран.
Отображение строки по номеру
Команда p (print) имеет такой формат:
[адрес1[,адрес2]]p
Для отображения строки входного файла достаточно указать ее номер, например:
$ ~ sed '2p' quote.txt
The honeysuckle band played all night long for only $90.
It was an evening of splendid music and company.
It was an evening of splendid music and company.
Too bad the disco floor fell through at 23:10.
The local nurse Miss P. Neave was in attendance.
$ ~
Что было сделано неправильно? Ведь требовалось отобразить только строку номер 2, однако в результате были выведены на экран все строки файла, причем вторая строка — дважды. Причина подобного поведения заключается в том, что по умолчанию редактор sed отображает каждую просматриваемую строку. Чтобы избежать этого, воспользуемся опцией -n:
$ ~ sed -n '2p' quote.txt
It was an evening of splendid music and company.
$ ~
Поиск специальных символов
Если требуется найти строку, содержащую символ '$', который в редакторе sed имеет специальное назначение, следует защитить этот символ от интерпретации с помощью обратной косой черты, как показано ниже:
$ ~ sed -n '/\$/ p' quote.txt
The honeysuckle band played all night long for only $90.
$ ~
Поиск первой строки
Для вывода первой строки входного файла достаточно указать ее номер:
$ ~ sed -n '1p' quote.txt
The honeysuckle band played all night long for only $90.
$ ~
Вывод номеров строк (команда =)
Команда = имеет следующий формат: [адрес] =
Она предназначена для вывода номера строки, соответствующей заданному адресу. Рассмотрим пример:
$ ~ sed '/music/=' quote.txt
The honeysuckle band played all night long for only $90.
2
It was an evening of splendid music and company.
Too bad the disco floor fell through at 23:10.
The local nurse Miss P. Neave was in attendance.
$ ~
В данном случае отображается весь файл, причем перед строкой, содержащей слово "music", выводится ее номер. Из этого можно сделать заключение, что команда = выполняется перед тем, как текущая строка будет выведена на экран. Если же требуется узнать только номер строки, задайте опцию -n:
$ ~ sed -n '/music/=' quote.txt
2
$ ~
Можно также отобразить и строку, и ее номер. Для этого следует воспользоваться опцией -e, позволяющей указать несколько команд подряд. Первая команда выводит строку, в которой найдено совпадение с шаблоном, а вторая — номер этой строки:
$ ~ sed -n -e '/music/p' -e '/music/=' quote.txt
It was an evening of splendid music and company.
2
$ ~
Отображение строк из заданного диапазона
Предположим, требуется вывести строки с номерами от 1 до 3. В этом случае следует указать два адреса, разделенные запятой:
$ ~ sed -n '1,3p' quote.txt
The honeysuckle band played all night long for only $90.
It was an evening of splendid music and company.
Too bad the disco floor fell through at 23:10.
$ ~
Поиск строк, соответствующих шаблону
В следующем примере показано, как найти строку, содержащую слово "Neave":
$ ~ sed -n '/Neave/p' quote.txt
The local nurse Miss P. Neave was in attendance.
$ ~
Поиск пo шаблону и номеру строки
Если адрес представлен в виде шаблона, редактор sed находит все строки, соответствующие этому шаблону. Как можно уточнить местонахождение строки?
Рассмотрим пример. Предположим, требуется найти слово "The" в последней строке файла quote.txt. Если воспользоваться поиском по шаблону, то будет получено две строки:
$ ~ sed -n '/The/p' quote.txt
The honeysuckle band played all night long for only $90.
The local nurse Miss P. Neave was in attendance.
$ ~
Чтобы остановить свой выбор на последней строке, следует указать ее номер перед шаблоном:
$ ~ sed -n '4,/The/p' quote.txt
The local nurse Miss P. Neave was in attendance.
$ ~
Поиск последней строки
Чтобы сослаться на последнюю строку входного файла, воспользуйтесь метасимволом '$':
$ ~ sed -n '$p' quote.txt
The local nurse Miss P. Neave was in attendance.
$ ~
Отображение всего файла
Если требуется отобразить весь файл, задайте диапазон строк от первой до последней:
$ ~ sed -n '1,$p' quote.txt
The honeysuckle band played all night long for only $90.
It was an evening of splendid music and company.
Too bad the disco floor fell through at 23:10.
The local nurse Miss P. Neave was in attendance.
$ ~
Добавление текста (команда а)
Для добавления текста предназначена команда a (append), которая вставляет одну или несколько строк текста после адресуемой строки. Формат команды таков:
[адрес] a\
текст\
текст\
..........
текст
Адрес может быть представлен в виде номера строки либо регулярного выражения. Во втором случае найденных строк может быть несколько. При добавлении текста отсутствует возможность задать диапазон строк. Допускается указание только одного шаблона адреса. Если адрес, по которому помещается текст, не указан, тогда команда будет применена к каждой строке входного файла.
Обратите внимание на присутствие символа обратной косой черты в конце каждой добавляемой строки, а также после самой команды а. Этот метасимвол защищает от интерпретации символ новой строки. В последней строке указывать обратную косую черту не требуется, поскольку концевой символ новой строки в этом случае является признаком конца команды.
Добавляемый текст записывается в стандартный выходной поток и не дублируется во входном буфере, поэтому не подлежит редактированию, т. е. на него нельзя сослаться в последующих шаблонах поиска. Чтобы иметь возможность отредактировать полученный текст, необходимо сохранить результаты работы редактора sed в новом файле и применить команды редактирования уже к нему.
Создание файла сценария
Конечно, ввод многострочных команд в режиме командной строки не слишком удобен и чреват ошибками, поэтому лучше всего размещать такие команды в файлах сценариев. Кроме того, в сценариях допускается наличие пустых строк и комментариев, что облегчает восприятие самих сценариев. Создадим новый файл с именем append.sed и добавим в него показанные ниже команды:
$ ~ cat > append.sed
#!/bin/sed -f
/company/a\
Then suddenly it happened.
^C
$ ~
Сделаем этот файл исполняемым:
$ chmod +x append.sed или chmod 744 append.sed
Выполненим:
$ ~ ./append.sed quote.txt
The honeysuckle band played all night long for only $90.
It was an evening of splendid music and company.
Then suddenly it happened.
Too bad the disco floor fell through at 23:10.
The local nurse Miss P. Neave was in attendance.
$ ~
Рассмотрим, что делает сценарий append.sed. Первая его строка является системной командой, которая указывает, какая программа выполняет данный сценарий. Формат этой команды мы уже рассматривали при знакомстве с файлами сценариев awk выше. Редактор sed, как правило, находится в каталоге /bin, в современных Linux он в добавок еще и хеширован:
$ type sed
sed is hashed (/bin/sed)
$ hash -l
builtin hash -p /usr/bin/figlet figlet
builtin hash -p /usr/bin/tput tput
builtin hash -p /bin/date date
builtin hash -p /bin/cat cat
builtin hash -p /bin/sed sed
$
Далее в сценарии находится команда а, которая ищет во входном файле строку, содержащую слово "company", и вставляет после нее предложение: Then suddenly it happened.
Вставка текста (команда i)
Команда i (insert) аналогична команде а, только вставляет текст не после, а перед адресуемой строкой. Как и при добавлении текста, допускается указание только одного шаблона адреса. Ниже приведен общий формат команды:
[адрес] i\
текст\
текст\
.........
текст
В следующем сценарии предложение "Utter contusion followed" вставляется перед строкой, содержащей слово "attendance":
$ ~ cat > insert.sed
#! /bin/sed -f
/attendance/i\
Utter confusion followed.
^C
$ ~ chmod 744 insert.sed
$ ~
Результаты работы данного сценария будут такими:
$ ~ cat quote.txt
The honeysuckle band played all night long for only $90.
It was an evening of splendid music and company.
Too bad the disco floor fell through at 23:10.
The local nurse Miss P. Neave was in attendance.
$ ~ ./insert.sed quote.txt
The honeysuckle band played all night long for only $90.
It was an evening of splendid music and company.
Too bad the disco floor fell through at 23:10.
Utter confusion followed.
The local nurse Miss P. Neave was in attendance.
$ ~
Для указания места вставки текста можно было бы воспользоваться номером строки, здесь 4:
#!/bin/sed -f
4i\
Utter confusion followed.
Удаление текста (команда d)
Для удаления текста предназначена команда d (delete), имеющая следующий формат:
[адрес1[, адрес2]] d
Адрес может быть указан в виде номера строки или регулярного выражения. Рассмотрим примеры. В первом из них будет удалена первая строка входного файла:
$ ~ sed '1d' quote.txt
It was an evening of splendid music and company.
Too bad the disco floor fell through at 23:10.
The local nurse Miss P. Neave was in attendance.
$ ~
В следующем примере удаляются строки 1—3:
$ ~ sed '1,3d' quote.txt
The local nurse Miss P. Neave was in attendance.
$ ~
В этом примере удаляется последняя строка:
$ ~ sed '$d' quote.txt
The honeysuckle band played all night long for only $90.
It was an evening of splendid music and company.
Too bad the disco floor fell through at 23:10.
$ ~
Можно также удалить строку, в которой найдено совпадение с регулярным выражением. В показанном ниже примере удаляется строка, содержащая слово '"Neave":
$ ~ sed '/Neave/d' quote.txt
The honeysuckle band played all night long for only $90.
It was an evening of splendid music and company.
Too bad the disco floor fell through at 23:10.
$ ~
Изменение текста (команда с)
Команда с (change) заменяет новым текстом каждую адресуемую строку. Если выбрана группа строк, вся группа заменяется одной копией текста. Формат команды с таков:
[адрес1[,адрес2]] c\
текст\
текст\
..........
текст
В следующем примере первая строга файла quote.txt заменяется новой строкой:
$ ~ cat > change.sed
#! /bin/sed -f
1c\
The Office Dibble band played well.
^C
$ ~ chmod 744 change.sed
$ ~
Итого:
$ ~ ./change.sed quote.txt
The Office Dibble band played well.
It was an evening of splendid music and company.
Too bad the disco floor fell through at 23:10.
The local nurse Miss P. Neave was in attendance.
$ ~
Команды изменения, добавления и вставки текста можно применять к одному и тому же файлу. Ниже приведен пример такого сценария, снабженный необходимыми комментариями:
$ ~ cat > mix.sed
#! /bin/sed -f
# Изменяем строку номер 1
1c\
The Dibble band were grooving.
# Вставляем строку
/evening/i\
They played some great tunes.
# Изменяем последнюю строку
$c\
Nurse Neave was too tipsy to help.
# Добавляем строку после строки номер 3
3a\
^C
$ ~ chmod 744 mix.sed
$ ~
Вот что получится в результате выполнения этого сценария:
$ ~ cat quote.txt
The honeysuckle band played all night long for only $90.
It was an evening of splendid music and company.
Too bad the disco floor fell through at 23:10.
The local nurse Miss P. Neave was in attendance.
$ ~ ./mix.sed quote.txt
The Dibble band were grooving.
They played some great tunes.
It was an evening of splendid music and company.
Too bad the disco floor fell through at 23:10.
Nurse Neave was too tipsy to help.
$ ~
Замена подстроки (команда s)
Команда s (substitute) осуществляет во всех адресуемых строках замену подстроки, соответствующей заданному шаблону, указанной подстрокой. Формат команды таков:
[адрес1[, адрес2]] s/шаблои_поиска/шаблбон_замены/[флаги]
Ниже перечислены возможные флаги. В следующем примере осуществляется замена слова "night" словом "NIGHT":
$ ~ sed -n 's/night/NIGHT/p' quote.txt
The honeysuckle band played all NIGHT long for only $90.
$ ~
Если требуется удалить из строки символ '$', оставьте шаблон замены пустым (не забывайте, что в редакторе sed знак доллара является метасимволом, поэтому он должен быть защищен обратным слешем.
$ ~ sed -n 's/\$//p' quote.txt
The honeysuckle band played all night long for only 90.
$ ~
Флаг g (global) позволяет выполнить глобальную подстановку шаблона замены на место шаблона поиска в пределах каждой адресуемой строки. Предположим, например, что мы хотим заменить все точки в файле quote.txt восклицательными знаками. Следующая команда выполнит работу не полностью:
$ ~ sed 's/\./!/' quote.txt
The honeysuckle band played all night long for only $90!
It was an evening of splendid music and company!
Too bad the disco floor fell through at 23:10!
The local nurse Miss P! Neave was in attendance.
$ ~
Обратите внимание на последнюю строку: в ней точка встречается дважды, но замене подвергся только первый символ. Для исправления подобной ситуации нужно указать флаг g:
$ ~ sed 's/\./!/g' quote.txt
The honeysuckle band played all night long for only $90!
It was an evening of splendid music and company!
Too bad the disco floor fell through at 23:10!
The local nurse Miss P! Neave was in attendance!
$ ~
С помощью флага w (write) можно указать файл, в который будут записаны все модифицируемые строки. В показанном ниже примере осуществляется замена слова "splendid" словом "SPLENDID", а все строки, где была выполнена эта замена, помещаются в файл sed.out.
$ ~ sed -n 's/splendid/SPLENDID/w sed.out' quote.txt
$ ~ cat sed.out
It was an evening of SPLENDID music and company.
$ ~
Ссылка на искомую подстроку с помощью метасимвола &
Метасимвол & позволяет сослаться в шаблоне замены на подстроку, соответствующую шаблону поиска. Например, в следующей команде слово "Miss" либо "miss" заменяется фразой "lovely Miss Joan" или "lovely miss Joan" соответственно:
$ ~ sed -n 's/[Mm]iss/lovely & Joan/p' quote.txt
The local nurse lovely Miss Joan P. Neave was in attendance.
$ ~
Заметьте, что пробелы также являются частью шаблона замены.
Чтение строк из файла (команда r)
В процессе обработки входного файла редактор sed позволяет читать текст из другого файла и добавлять его к текущему содержимому буфера, размещая после каждой строки, соответствующей шаблону адреса. Формат предназначенной для этого команды r (read) таков:
[адрес] r имя_файла
Давайте создадим небольшой файл с именем sedex.txt.
$ ~ cat > sedex.txt
Boom boom went the music.
^C
$ ~
В следующем примере содержимое этого файла выводится на экран после строки файла quote.txt, содержащей слово "company":
$ ~ sed '/company/r sedex.txt' quote.txt
The honeysuckle band played all night long for only $90.
It was an evening of splendid music and company.
Boom boom went the music.
Too bad the disco floor fell through at 23:10.
The local nurse Miss P. Neave was in attendance.
$ ~
Вывод строк в файл (команда w)
Подобно тому как оператор > применяется для перенаправления результатов работы программы в файл, команда w (write) редактора sed позволяет записать в указанный файл строки, отобранные по заданному шаблону адреса. Формат этой команды таков:
[адрес1[, адрес2]] w имя_файла
Если файл не существует, он будет создан, если существует — его содержимое будет перезаписано. Если в сценарии встречается несколько команд w, направляющих результаты в один и тот же файл, данные всех команд, кроме первой, будут добавляться в конец файла. Рассмотрим пример:
$ ~ sed '1,2w sed.out' quote.txt
The honeysuckle band played all night long for only $90.
It was an evening of splendid music and company.
Too bad the disco floor fell through at 23:10.
The local nurse Miss P. Neave was in attendance.
$ ~ cat sed.out
The honeysuckle band played all night long for only $90.
It was an evening of splendid music and company.
$ ~
Здесь содержимое файла quote.txt выводится на экран, а строки с номерами 1 и 2 отправляются в файл с именем sed.out.
В следующем примере осуществляется поиск строки, содержащей слово "Neave", и если такая строка найдена, она записывается в файл sed.out.
$ ~ sed -n '/Neave/w sed.out' quote.txt
$ ~ cat sed.out
The local nurse Miss P. Neave was in attendance.
$ ~
Досрочное завершение работы (команда q)
Иногда требуется завершить работу редактора sed сразу же после нахождения первого совпадения с шаблоном. Эту задачу решает команда q (quit), имеющая следующий формат: [адрес] q
Обратимся к примеру. Допустим, требуется осуществить поиск строки, содержащей такой шаблон: /\<.a.\{0,2\}\>/
Этому шаблону соответствует любое слово (выражение \< обозначает начало слова, а выражение \> его конец), в котором вторым символом является буква 'a', а за ней идет не более двух символов.
В файле quote.txt таких слов четыре:
строка 1 — band,
строка 2 — was,
строка 3 — bad,
строка 4 — was.
Показанная ниже команда находит строку, в которой шаблон встречается первый раз, после чего завершает работу:
$ ~ sed '/\<.a.\{0,2\}\>/q' quote.txt
The honeysuckle band played all night long for only $90.
$ ~
Отображение управляющих символов (команда l)
Иногда даже в текстовых файлах содержатся различного рода непечатаемые символы. Это может быть следствием неправильного ввода данных в текстовом редакторе или ошибок конвертации при загрузке файлов из других систем. При выводе таких файлов. на экране могут быть получены странные результаты, когда вместо непечатаемого символа отображается один или несколько обычных символов непонятного происхождения. Разобраться в таких ситуациях помогает команда cat -v, которая помечает начало замещающей последовательности символом '^' (знак крышки). Предположим, вы обнаружили незнакомый файл func.txt и хотите узнать его содержимое:
$ ~ cat func.txt
This is the F1 key:Маркер в таблицах
This is the F2 key:Символ якоря
$ ~ cat -v func.txt
This is the F1 key:M-PM-^\M-PM-0M-QM-^
This is the F2 key:M-PM-!M-PM-8M-PM-<M
$ ~
Аналогичным образом будет вести себя и редактор sed при работе с данным; файлом. Если вы попытаетесь просмотреть его содержимое, будет выдано следующее:
$ ~ sed -n '1,$p' func.txt
This is the F1 key:Маркер в таблицах
This is the F2 key:Символ якоря
$ ~
В редакторе существует команда l (list}, аналог рассмотренной выше команды cat -v.
Формат команды l таков: [адрес1[,адрес2]] l
Ее действие равносильно применению команды p, но при этом все непечатаемые символы заменяются восьмеричными ASCII–кодами (кроме того, длинные строки, выходящие за пределы экрана, разбиваются на части, а конец каждой строки помечается символом '$'). Вот что получится, если применить эту команду к файлу func.txt.
$ ~ sed -n '1,$l' func.txt
This is the F1 key:\320\234\320\260\321\200\320\272\320\265\321\200 \
\320\262 \321\202\320\260\320\261\320\273\320\270\321\206\320\260\321\
\205 $
This is the F2 key:\320\241\320\270\320\274\320\262\320\276\320\273 \
\321\217\320\272\320\276\321\200\321\217\t$
$ ~
Обработка управляющих символов
Одной из задач, для которых редактор sed используется весьма часто, является удаление управляющих, непечатаемых и просто посторонних символов из файлов, которые были загружены из других систем. Ниже приведен фрагмент подобного файла dos.txt.
$ cat -v dos.txt
12332##DISO##45.12^M C0332##LPSO##23.11^M
01299##USPD##34.46^M
Вот что необходимо сделать:
Заменить все знаки решетки ('#'( пробелом.
Удалить из первого поля каждой строки все ведущие нули.
Удалить в конце каждой строки последовательность ^M, генерируемую непечатаемым символом CR — возврат каретки (ASCII–код 13).
Примечание: В ряде систем, в которых мне приходилось выполнять подобные преобразования, в конце строки стоит символ перевода строки LF (ASCII–код 10, отображается как ^@). Подобная проблема нередко возникает при передаче файлов между двумя системами, в одной из которых символ новой строки (\n) заменяется последовательностью CR/LF, a в другой — только одним из этих двух управляющих символов.
Задача 1. Удаление всех символов решетки реализуется без особого труда. Для этого достаточно выполнить команду замены s с флагом глобальной подстановки д, указав при этом, что один или более символов '#', идущих подряд, должны быть заменены пробелом:
$ sed 's/##*/ /g' dos.txt | cat -v
12332 DISO 45.12^М
00332 LPSO 23.11^М
01299 USPD 34.4б^M
Задача 2. Для удаления всех ведущих нулей следует в команде s оставить шаблону замены пустым, а шаблон поиска задать таким: /^0*/. Он означает, что требуется найти любое количество нулей, стоящих в начале строки.
$ sed 's/^0*//' dos.txt | cat -v
12332##DISO##45.12^M
332##LPSO##23.11^M
1299##USPD ##34.46^M
Задача 3. Чтобы избавиться от управляющих символов ^M в конце строк, необходимо. также применить команду s, оставив шаблон замены пустым. А вот при формировании шаблона поиска следует учесть, что мы имеем дело с непечатаемым символом. Нельзя просто ввести символы '^' и 'M', так как полученный шаблон будет означать, что мы ищем букву 'М', стоящую в начале строки. Чтобы действительно создать нужный нам непечатаемый символ, необходимо нажать [Ctrl+V], а затем — клавишу [Enter]. Результат будет выглядеть как регулярное выражение ^M, но на самом деле это экранное представление символа CR.
$ sed 's/^M//' dos.txt | cat -v
12332##DIS0##45.12
00332##LPSO##23.11
01299##U5PD#t34.46
Теперь можно попробовать объединить три команды в одну с помощью опции -e;
$ sed -e 's/^0*' -e 's/^M' -e 's/##*/ /g' dos.txt | cat -v
12332 DISO 45.12
332 LPSO 23.11
1299 USPD 34.46
Выходные данные редактора sed передаются по каналу команде cat -v, которая позволяет убедиться, что вся работа, включая удаление непечатаемых символов, выполнена правильно.
Приведенные выше команды удобнее поместить в файл сценария. Назовем его dos.sed. Вот его текст:
$ cat dos.sed
#! /bin/sed -f
#Имя: dos.sed
#Командная строка: dos.sed dos.txt
#Избавляемся от символа решетки
s/##*/ /g
#Удаляем ведущие нули
s/^0*//
#Удаляем символы возврата каретки
s/^M//
На входе данного сценария следует указать файл dos.txt. Сценарий "исправит" этот файл и выведет результат на экран. Если же ввести следующую командную строку:
$ dos.sed dos.txt | tee dos.txt
то результат будет записан обратно в файл dos.txt.
Обработка отчетов
Я часто сталкиваюсь с необходимостью форматировать данные, возвращаемые инструкциями SQL. Для этого приходится писать комплексные сценарии, в которых сразу несколько текстовых фильтров выполняют работу совместно. Рассмотрим результаты выполнения некоторой инструкции SQL, выполняющей обращение к одной из таблиц базы данных:
Database Size (MB) Date created
-----------------------------------------------------
GOSOUTH 2244 12/11/97
TRISUD 5632 8/9/99
(2 rows affected)
Из этой информации нас, предположим, интересуют только имена баз данных, находящиеся в первой колонке. Чтобы их извлечь, необходимо выполнить следующую последовательность действий:
Удалить пунктирную линию с помощью команды s/--*//g.
Удалить все пустые строки с помощью команды /^$/d.
Удалить последнюю строку с помощью команды $d.
Удалить первую строку с помощью команды 1d.
Отобразить первый столбец с помощью команды awk '{print $1}'. Ниже приведена соответствующая цепочка команд:
$ sed 's/--*//g' -e '/^$/d' -e '$d' -e '1d' sql.txt | awk '{print $1}'
GOSOUTH
TRISUD
Добавление текста
В процессе потоковой обработки файла мне иногда требуется добавить к каждой проверенной строке какой‑нибудь текст, сообщающий о том, как прошла обработка. Предположим, имеется такой файл:
$ ~ cat > ok.txt
АС456
АС492169
АС9967
АС88345
^C
$ ~ sed 's/$/ Passed/' ok.txt
АС456 Passed
АС492169 Passed
АС9967 Passed
АС88345 Passed
$ ~
Наша задача состояла в добавлении слова "Passed" (обработано) в конец каждой строки. Решить ее несложно. Достаточно в шаблоне поиска указать метасимвол '$', означающий конец строки, а в шаблоне замены — пробел и искомое слово: Passed
Вставить новую строку
$ sed '3 a new line content' testfile
вставит строку после указанного номера здесь 3
$ sed '$ a new line content' testfile
добавит в конец, символ $
$ sed '/PATTERN/ a new line content' testfile
вставит после некого содержания (патерна)
И все это хорошо, только выходной файл останется не измененным и это удобно если мы работаем только с stdin, чтобы изменения остались в файле надо воспользоваться опцией -i
Удаление начальной косой черты в путевом имени
Ниже показано, как с помощью редактора sed можно быстро удалить начальный слеш из имени текущего каталога:
$ ~ cd /usr/local
$ echo $PWD | sed 's/^\///g'
usr/local
$ local
Имя текущего каталога хранится в переменной среды $PWD. Эта переменная обновляется всякий раз, когда выполняется команда cd. Команда echo передает значение переменной по каналу редактору sed, который выполняет несложную обработку: находит в начале строки (метасимвол '^'( символ косой черты (защищен от интерпретации обратной косой чертой) и заменяет его пустой подстрокой.
Объединение команд
Ясо дело можно пользоваться каналом:
$ sed '.........' | sed '...........'
...,но это не эстетично, для этого существует опция -e которую надо вставить перед каждой командой ("патерном"):
$ sed -e '.........' -e '...........'
..., ну и кроме того точка с запятой конечно:
$ sed '.........; ...........'
....,при использовании опции –e возникает необходимость разрывать одиночные кавычки, а при использовании точки с запятой все команды можно перечислить в одних кавычках.
$ { speedtest-cli | egrep 'Download:|Upload:'; date; iw dev | grep ssid; } | sed -e 's/ssid/SSID:/g' -e '$ a === end of report ===' | tee -a speedtest
https://sites.google.com/site/kfgnb0101
PS: В данном случае использование флага -i не нужно поскольку используется "чудесная" утилита tee, которая перенаправит stdin в указанный файл....вообще наверное следует избегать всякого рода "терминантов" в середине выражения, а ставить их в конец и работать сначала с потоками (что вполне естественно).
Разбить строку на абзацы
$ ~ echo "sdf2sfd2sd2" | sed -e 's/2/\n/g'
sdf
sfd
sd
$ ~ echo "hjkhjk'huhjkhjk'hjkhjkhjk'jkljkl" | sed "s/'/\n/g"
hjkhjk
huhjkhjk
hjkhjkhjk
jkljkl
$ ~ echo "hjkhjk'huhjkhjk'hjkhjkhjk'jkljkl" | tr "\'" "\n"
hjkhjk
huhjkhjk
hjkhjkhjk
jkljkl
$ ~
$ ~ echo "abcdefgjijklmnop" | sed 's/.\{3\}/&\n/g'
abc
def
gji
jkl
mno
p
$ ~
Примеры операций в sed
8d ------------------------ Удалить 8-ю строку.
/^$/d ----------------------Удалить все пустые строки.
1,/^$/d --------------------Удалить все строки до первой пустой строки, включительно.
/Jones/p ------------------Вывести строки, содержащие "Jones" (с ключом -n).
s/Windows/Linux/ ----В каждой строке, заменить первое встретившееся слово "Win" на слово "Lin".
s/BSOD/stability/g ----В каждой строке, заменить все встретившиеся слова "BSOD" на "stability".
s/ *$// ----------------------Удалить все пробелы в конце каждой строки.
s/00*/0/g -----------------Заменить все последовательности ведущих нулей одним символом "0".
/GUI/d --------------------Удалить все строки, содержащие "GUI".
s/GUI//g --------------Удалить все найденные "GUI", оставляя остальную часть строки без изменений.
http://citforum.ru/operating_systems/articles/tut_6.shtml
http://ant0.ru/sed1line.html <<------- SED on line Eric Pement (RU)
http://www.pement.org/sed/sed1line.txt <<------- SED on line Eric Pement
http://www.pement.org/awk/awk1line.txt <<------- AWK on line Eric Pement
Редактор sed является эффективным инструментом фильтрации текста. Он дает возможность находить во "входном потоке" требуемые строки и последовательности символов и модифицировать их с наименьшими затратами времени и усилий. Как было показано, для извлечения из файлов требуемой информации вовсе не обязательно прибегать к написанию больших сценариев.
Мы коснулись лишь основ работы с sed, но и полученных знаний достаточно, чтобы с помощью sed решать многие задачи, возникающие в процессе обработки текста.
Утилита tr во многом дополнит фильтрацию и разбор "входного потока" и во многих случаях заменит использование sed.