BASH: используем интерпретатор awk. AWK: Примеры программ

Основное назначение программы awk - интерпретация языка программирования awk, который позволяет быстро создать программу для анализа и преобразования текстовых файлов. Типичный awk-сценарий построен по следующей схеме:

шаблон1 {действие1} шаблон2 {действие2} ...

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

Печать тертьего поля (колонки, слова) каждой строки:

Код:

Ls -l | awk "{print($3)}"

Печать двух указанных полей каждой строки:

Код:

Ls -l | awk "{print($9,$1)}"

Печать полей с пробелами:

Код:

Ls -l | awk "{print($9," ",$1)}"

Чтобы указать разделитель полей, отличный от пробела, следует использовать параметр -F. В данном случае разделителем полей будет двоеточие:

Код:

Awk -F: "{print($2)}" $filenameForProcessing

Чтобы использовать скрипт awk, сохранённый в файле:

Код:

Awk -f $scriptFilename $filenameForProcessing

Файл скрипта awk можно сделать исполняемым и указать в нём соответствующий ша-банг. Такой скрипт будет принимать в виде параметра файл для обработки:

Код:

#!/usr/bin/awk -f

Переменные языка awk создаются в момент первого обращения к ним и могут содержать целые числа, числа с плавающей запятой или строки, что определяется контекстом. В специальной переменной RS хранится значение разделителя записей (по умолчанию - \n), а в переменной FS - значение разделителя полей (по умолчанию - пробел). Если какая-либо из этих переменныех содержит более, чем один символ, это значение интерпретируертся как регулярное выражение. Язык awk содержит ряд встроенных строковых и математических функций, условные операторы и операторы циклов, поддерживает массивы и определение пользовательских функций. В интернете можно найти обширные руководства по языку awk, а также автоматические трансляторы ("переводчики") скриптов awk на другие языки (например, Си или Перл).

Специальными видами шаблонов являются BEGIN и END. Они не проверяются на соответствие с записями входного потока. Действие по шаблону BEGIN будет выполнено один раз до начала чтения входных данных, а действие по шаблону END - один раз после прочтения входных данных.

Пример удаления соседних дубликатов строк в файле:

Код:

Filename=test.txt
res=`awk "BEGIN {PREV=""} {if ($0 != PREV) {print $0; PREV=$0}}" $filename`
echo "$res" > $filename

Перед началом работы мы устанавливаем переменную PREV равной пустой строке. Остальная часть скрипта awk не имеет шаблона и поэтому выполняется для любой строки. Если текущая строка не равна PREV, она выводится, а затем записывается в переменную PREV. Таким образом, при обработке каждой строки, если она окажется равна предыдущей, она выведена не будет.

Пример конкатенации полей:

Код:

Awk "{a = $3 $4; print a}" $filename

Пример суммирования значений полей:

Код:

Awk "{a = $3+$4; print a}" $filename

Понятие "селектор" следует понимать, как расширение понятия "шаблон". Там, где в структуре команды указан шаблон, в общем случае может стоять любой селектор.

Проверка третьего поля на соответствие регулярному выражению и печать всей строки в случае успеха:

Код:

Awk "$3~/(7)$/ {print}" $filename

Проверка первого поля на равенство указанной строке и печать второго поля в случае успеха.

Введение в замечательный язык со странным именем

Серия контента:

В защиту awk

В этой серии статей я собираюсь сделать из читателя искусного программиста на awk. Я согласен, что у awk не самое приятное и модное имя, а GNU-версия awk, названная gawk, звучит откровенно странно. Незнакомые с этим языком программисты, услышав его название, возможно, представят себе мешанину древнего и устаревшего кода, способного довести до умопомрачения даже самого знающего специалиста по UNIX (заставив его восклицать "kill -9!" и беспрестанно бегать за кофе).

Да, у awk отнюдь не замечательное имя. Но это замечательный язык. Awk создан для обработки текста и создания отчетов, но у него много хорошо проработанных функций, дающих возможность серьезного программирования. При этом, в отличие от некоторых других языков, синтаксис awk привычен и заимствует лучшее из таких языков, как C, python и bash (хотя формально awk был создан до python и bash). Awk - один из тех языков, которые, будучи один раз выучены, становятся ключевой частью стратегического арсенала программиста.

Первый шаг в awk

Давайте начнем и попробуем поэкспериментировать с awk, чтобы увидеть, как он работает. В командной строке введем следующую команду:

$ awk "{ print }" /etc/passwd

В результате должно быть показано содержимое файла /etc/passwd. Теперь - объяснение того, что делал awk. Вызывая awk, мы указали /etc/passwd в качестве входного файла. Когда мы запустили awk, он обработал команду print для каждой строки в /etc/passwd по порядку. Весь вывод отправлен в stdout, и мы получили результат, идентичный результату команды cat /etc/passwd. Теперь объясним блок { print } . В awk фигурные скобки используются для группирования блоков текста, как в C. В нашем блоке текста есть лишь одна команда print. В awk команда print без дополнительных параметров печатает все содержимое текущей строки.

Вот еще один пример программы на awk, которая делает то же самое:

$ awk "{ print $0 }" /etc/passwd

В awk переменная $0 представляет всю текущую строку, поэтому print и print $0 делают в точности одно и то же. Если угодно, можно создать программу на awk, которая будет выводить данные, совершенно не связанные с входными данными. Вот пример:

$ awk "{ print "" }" /etc/passwd

При передаче строки "" команде print она всегда печатает пустую строку. Если протестировать этот скрипт, обнаружится, что awk выводит одну пустую строку на каждую строку в файле /etc/passwd. Это опять-таки происходит потому, что awk исполняет скрипт для каждой строки во входном файле. Вот еще один пример:

$ awk "{ print "hiya" }" /etc/passwd

Если запустить этот скрипт, он заполнит экран словами «ура». :)

Множественные поля

Awk хорошо подходит для обработки текста, разбитого на множество логических полей, и дает возможность без усилий обращаться к каждому отдельному полю из awk-скрипта. Следующий скрипт распечатает список всех учетных записей в системе:

$ awk -F":" "{ print $1 }" /etc/passwd

В вызове awk в вышеприведенном примере параметр –F задает ":" в качестве разделителя полей. Обрабатывая команду print $1, awk выводит первое поле, встреченное в каждой строке входного файла. Вот еще один пример:

$ awk -F":" "{ print $1 $3 }" /etc/passwd

Вот фрагмент из вывода на экран этого скрипта:

halt7 operator11 root0 shutdown6 sync5 bin1 ....etc.

Как видим, awk выводит первое и третье поля файла /etc/passwd, которые представляют собой соответственно поля имени пользователя и uid. При этом, хотя скрипт и работает, он не совершенен - нет пробелов между двумя выходными полями! Те, кто привык программировать в bash или python, возможно ожидали, что команда print $1 $3 вставит пробел между этими двумя полями. Однако когда в программе на awk две строки оказываются рядом друг с другом, awk сцепляет их без добавления между ними пробела. Следующая команда вставит пробел между полями:

$ awk -F":" "{ print $1 " " $3 }" /etc/passwd

Когда print вызывается таким способом, он последовательно соединяет $1 , " " и $3 , создавая удобочитаемый вывод на экране. Конечно, мы можем также вставить метки полей, если нужно:

$ awk -F":" "{ print "username: " $1 "\t\tuid:" $3" }" /etc/passwd

В результате получаем такой вывод:

username: halt uid:7 username: operator uid:11 username: root uid:0 username: shutdown uid:6 username: sync uid:5 username: bin uid:1 ....etc.

Внешние скрипты

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

$ awk -f myscript.awk myfile.in

Размещение скриптов в отдельных текстовых файлах также позволяет воспользоваться дополнительными преимуществами awk. Например, следующий многострочный скрипт делает то же самое, что и один из наших предыдущих однострочных - распечатывает первое поле каждой строки из /etc/passwd:

BEGIN { FS=":" } { print $1 }

Разница между этими двумя методами состоит в том, как мы задаем разделитель полей. В этом скрипте разделитель полей указывается внутри самой программы (установкой переменной FS), тогда как в нашем предыдущем примере FS настраивается путем передачи awk параметра -F":" в командной строке. Обычно лучше всего задавать разделитель полей внутри самого скрипта, просто потому, что тогда не потребуется запоминать ещё один аргумент командной строки. Позже в этой статье мы рассмотрим переменную FS более подробно.

Блоки BEGIN и END

Обычно awk выполняет каждый блок в тексте скрипта один раз для каждой входной строки. Однако в программировании часто встречаются ситуации, когда требуется выполнить код инициализации перед тем, как awk начнет обрабатывать текст из входного файла. Для таких случаев awk дает возможность определять блок BEGIN. Мы использовали блок BEGIN в предыдущем примере. Поскольку блок BEGIN обрабатывается до того, как awk начинает обрабатывать входной файл, это отличное место для инициализации переменной FS (разделитель полей), вывода заголовка или инициализации других глобальных переменных, которые будут позже использоваться в программе.

Awk также предоставляет еще один специальный блок, называемый блоком END. Awk выполняет этот блок после того, как все строки во входном файле были обработаны. Обычно блок END используется для выполнения заключительных вычислений или вывода итогов, которые должны появиться в конце выходного потока.

Регулярные выражения и блоки

Awk позволяет использовать регулярные выражения для избирательного выполнения отдельных блоков программы в зависимости от того, совпадает или нет регулярное выражение с текущей строкой. Вот пример скрипта, который выводит только те строки, которые содержат символьную последовательность foo:

/foo/ { print }

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

/+\.*/ { print }

Выражения и блоки

Есть много других способов избирательно выполнять блок программы. Мы можем поместить перед блоком программы любое булево выражение для управления исполнением этого блока. Awk будет выполнять блок программы, только если предыдущее булево выражение равно true. Следующий пример скрипта будет выводить третье поле всех строк, в которых первое поле равно fred . Если первое поле текущей строки не равно fred , awk продолжит обработку файла и не выполнит оператор print для текущей строки: :

$1 == "fred" { print $3 }

Awk предлагает полный набор операторов сравнения, в том числе обычные "==", "<", ">", "<=", ">=" и "!=". Кроме того, awk предоставляет операторы "~" и "!~", которые означают "совпадает" и "не совпадает". При их использовании переменная помещается слева от оператора, а регулярное выражение - справа от него. Вот пример, где выводится только третье поле строки, если пятое поле той же строки содержит символьную последовательность root:

$5 ~ /root/ { print $3 }

Условные операторы

Awk предоставляет также очень приятные C-подобные операторы if. При желании можно переписать предыдущий скрипт с использованием if:

{ if ($5 ~ /root/) { print $3 } }

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

Вот более сложный пример оператора if в awk. Как можно видеть, даже в случае сложных вложенных условных выражений операторы if выглядят идентично их аналогам в C:

{ if ($1 == "foo") { if ($2 == "foo") { print "uno" } else { print "one" } } else if ($1 == "bar") { print "two" } else { print "three" } }

Используя операторы if, мы можем преобразовать этот код:

! /matchme/ { print $1 $3 $4 } { if ($0 !~ /matchme/) { print $1 $3 $4 } }

Оба скрипта распечатают только те строки, которые не содержат символьную последовательность matchme . И в этом случае тоже можно выбрать метод, который лучше работает в конкретной программе. Они оба делают одно и то же.

Awk также дает возможность использовать булевы операторы "||" ("логическое ИЛИ") и "&&"("логическое И"), что позволяет создавать более сложные булевы выражения:

($1 == "foo") && ($2 == "bar") { print }

Этот пример выведет только те строки, в которых первое поле равно foo и второе поле равно bar .

Числовые переменные!

До сих пор мы распечатывали либо строковые переменные, либо целые строки, либо конкретные поля. Однако awk также дает нам возможность выполнять сравнение как целых чисел, так и чисел с плавающей запятой. Используя математические выражения, очень легко написать скрипт, который считает число пустых строк в файле. Вот один такой скрипт:

BEGIN { x=0 } /^$/ { x=x+1 } END { print "Найдено " x " пустых строк. :)" }

В блоке BEGIN мы инициализируем нашу целочисленную переменную x значением ноль. Затем каждый раз, когда awk встречает пустую строку, он будет выполнять оператор x=x+1 , увеличивая x на 1. После того как все строки будут обработаны, будет выполнен блок END, и awk выведет конечный итог, указав число найденных пустых строк.

Строчные переменные

Одной из приятных особенностей переменных awk является то, что они "простые и строчные." Я называю переменные awk "строчными", потому что все переменные awk внутри хранятся как строки. В то же время переменные awk "простые", потому что с переменной можно производить математические операции, и если она содержит правильную числовую строку, awk автоматически позаботится о преобразовании строки в число. Чтобы понять, что я имею в виду, взглянем на этот пример:

x="1.01" # Мы сделали так, что x содержит *строку* "1.01" x=x+1 # Мы только что прибавили 1 к *строке* print x # Это, кстати, комментарий:)

Awk выведет:

2.01

Любопытно! Хотя мы присвоили переменной x строковое значение 1.01, мы все же смогли прибавить к ней единицу. Нам бы не удалось сделать это в bash или python. Прежде всего, bash не поддерживает арифметику с плавающей запятой. И, хотя в bash есть "строчные" переменные, они не являются "простыми"; для выполнения любых математических операций bash требует, чтобы мы заключили наши вычисления в уродливые конструкции $() . Если бы мы использовали python, нам бы потребовалось явно преобразовать нашу строку 1.01 в значение с плавающей запятой, прежде чем выполнять какие-либо расчеты с ней. Хоть это и не трудно, но это все-таки дополнительный шаг. В случае с awk все это делается автоматически, и это делает наш код красивым и чистым. Если бы нам потребовалось возвести первое поле каждой входной строки в квадрат и прибавить к нему единицу, мы бы воспользовались таким скриптом:

{ print ($1^2)+1 }

Если немного поэкспериментировать, то можно обнаружить, что если в какой-то переменной не содержится правильного числа, awk при вычислении математического выражения будет обращаться с этой переменной как с числовым нулем.

Множество операторов

Еще одна приятная особенность awk - это полный комплект математических операторов. Кроме стандартных сложения, вычитания, умножения и деления, awk дает нам возможность использовать ранее продемонстрированный оператор показателя степени "^", оператор остатка целочисленного деления "%" и множество других удобных операторов присваивания, заимствованных из C.

К ним относятся пред- и постинкрементные/декрементные операторы присваивания (i++, --foo), операторы присваивания со сложением/вычитанием/умножением/делением (a+=3, b*=2, c/=2.2, d-=6.2). Но это ещё не все - мы имеем также удобные операторы присваивания с вычислением остатка целочисленного деления и возведением в степень (a^=2, b%=4).

Разделители полей

В awk есть свой собственный комплект специальных переменных. Некоторые из них дают возможность тонкой настройки работы awk, а другие содержат ценную информацию о вводе. Мы уже затронули одну из этих специальных переменных, FS. Как упоминалось ранее, эта переменная позволяет задать последовательность символов, которую awk будет считать разделителем полей. Когда мы использовали в качестве ввода /etc/passwd, FS была установлена в ":". Это оказалось достаточно, но FS предоставляет нам еще большую гибкость.

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

FS="\t+"

Выше мы использовали специальный символ регулярных выражений "+", который означает "одно или несколько вхождений предыдущего символа".

Если поля разделены пустой областью (один или несколько пробелов или символов табуляции), возможно, вам захочется установить для FS следующее регулярное выражение:

FS="[[:space:]+]"

Хотя такая настройка сработает, в ней нет необходимости. Почему? Потому что по умолчанию значение FS равно одному символу пробела, который awk интерпретирует как "один или несколько пробелов или символов табуляции". В нашем конкретном примере значение FS по умолчанию именно такое, как нам было нужно!

Со сложными регулярными выражениями также не возникает проблем. Даже если записи разделены словом "foo", за которым следуют три цифры, следующее регулярное выражение позволит правильно разобрать данные:

FS="foo"

Число полей

Следующие две переменные, которые мы собираемся рассмотреть, обычно не предназначены для записи в них, а используются для чтения и получения полезной информации о вводе. Первая из них - переменная NF, называемая также "число полей". Awk автоматически устанавливает значение этой переменной равным числу полей в текущей записи. Можно использовать переменную NF для отображения только определенных входных строк:

NF == 3 { print "в этой записи три поля: " $0 }

Конечно, переменную NF можно использовать и в условных операторах, например:

{ if (NF > 2) { print $1 " " $2 ":" $3 } }

Номер записи

Еще одна удобная переменная - номер записи (NR). Она всегда содержит номер текущей записи (awk считает первую запись записью номер 1). До сих пор мы имели дело с входными файлами, которые содержали одну запись на строку. В таких ситуациях NR также сообщит номер текущей строки. Однако когда мы начнем обрабатывать многострочные записи в следующих статьях этой серии, это уже будет не так, поэтому нужно проявлять осторожность! NR можно использовать подобно переменной NF для вывода только определенных строк ввода:

(NR < 10) || (NR > 100) { print "Мы на записи номер 1-9 или 101 и более" }

Еще один пример:

{ #skip header if (NR > 10) { print "вот теперь пошла настоящая информация!" } }

Awk предоставляет дополнительные переменные, которые могут быть использованы для различных целей. Мы рассмотрим эти переменные в следующих статьях. Мы подошли к концу нашего начального исследования awk. В следующих статьях серии я покажу более сложную функциональность awk, и мы закончим эту серию awk-приложением из реальной практики. А пока, если хочется узнать больше, можно просмотреть ресурсы, перечисленные ниже.

Awk, главным образом, это потоковый редактор вроде sed. Вы можете передавать по трубе текст в эту программу, и она может манипулировать им построчно. Программа также может читать из файла. Ещё awk - это язык программирования. Это в основном означает, что awk может делать всё, что может sed, а также ещё многое другое.

В отличие от sed, awk может помнить контекст, делать сравнения и многие другие вещи, которые могут делать другие языки программирования. Например, она не ограничена единичной строкой. При надлежащей сноровке, она может ОБЪЕДИНЯТЬ множество строк.

Самая простая форма awk выглядит так:

Awk "{ здесь_какое-то_действие }"

"Здесь_какое-то_действие" может быть просто выражением для печати результата или чем-то намного более сложным. Синтаксис похож на язык программирования "C". Простой пример:

Awk "{print $1,$3}"

означает напечатать первый и третий столбец, где под столбцами понимаются «вещи, разделённые белым пробелом». Белый пробел = табуляция или пробел.

Живой пример:

Echo "1 2 3 4" | awk "{print $1,$3}" 1 3

Часть вторая: Что может делать AWK?

Главная цель в жизни AWK - это манипулировать её вводом на построчной основе. Программа awk обычно работает в стиле

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

Обычный используемый в программировании awk синтаксис можно описать так:

Awk образец {команда(ы)}

Это означает, что

«Посмотреть на каждую строку ввода, нет ли там ОБРАЗЦА. Если он там есть, запустить то, что между {}»

Можно пропустить или ОБРАЗЕЦ или КОМАНДУ

Если не указать образец, то команда будет применяться к КАЖДОЙ строке.

Если пропущена команда, то это эквивалентно указанию (просто напечатать строку):

{ print }

Конкретные примеры:

Awk "/#/ {print "В этой строке есть комментарий"}" /etc/hosts

будет печатать «В этой строке есть комментарий» для каждой строки, которая содержит хотя бы один "#" в любом месте строки в /etc/hosts

Модификация для наглядности

Awk "/#/ {print $0 ":\tВ этой строке есть комментарий"}" /etc/hosts

Элемент "//" в образце - это один из способов задать совпадение. Есть также другие способы задать, совпадает ли строка. Например,

Awk "$1 == "#" {print "строка начинается с хеша"}" /etc/hosts

будет соответствовать строкам, первый столбец в которых является единичным "#". Последовательность символов "==" означает ТОЧНОЕ СОВПАДЕНИЕ ВСЕГО первого столбца.

Модификация для наглядности:

Awk "$1 == "#" {print $0 "\tстрока начинается с хеша"}" /etc/hosts

С другой стороны, если вы хотите частичное совпадение конкретного столбца, используйте оператор "~"

Awk "$1 ~ /#/ {print "ГДЕ-ТО в столбце 1 есть хеш"}" /etc/hosts

ПОМНИТЕ, ЧТО ПЕРВЫЙ СТОЛБЕЦ МОЖЕТ БЫТЬ ПОСЛЕ БЕЛОГО ПРОБЕЛА.

Модификация для наглядности:

Awk "$1 ~ /#/ {print $0 "\tГДЕ-ТО в столбце 1 есть хеш"}" /etc/hosts

Ввод "# comment" будет соответствовать

Ввод " # comment" будет ТАКЖЕ соответствовать

Если вам нужно конкретное совпадение «строка, которая начинается точно с # и пробела», вы должны использовать

Awk "/^# / {делай что-то}"

Множественное совпадение

Awk обработает ВСЕ ОБРАЗЦЫ, которые соответствуют текущей строке. Поэтому если использовать следующий пример,

Awk " /#/ {print "Есть комментарий"} $1 == "#" {print "Комментарий в первом столбце"} /^# / {print "Комментарий в самом начале"} " /etc/hosts

ТРИ записи будет выведено для строки вроде следующей:

# This is a comment

ДВЕ записи для

# This is an indented comment

и только одна для

1.2.3.4 hostname # a final comment

Отслеживание контекста

Не все строки созданы равными, даже если они выглядят одинаково. Иногда вы хотите сделать что-то со строкой в зависимости от строк, которые идут перед ней.

Здесь быстрый пример, который печатает строки "ADDR" если вы не в секции "secret"

Awk " /secretstart/ { secret=1} /ADDR/ { if(secret==0) print $0 } /* $0 - это полная строка */ /secretend/ { secret=0} "

Следующее напечатает содержимое, которое содержит внутри "ADDR" кроме случаев, если была увидена строка "secretstart". ПОРЯДОК ИМЕЕТ ЗНАЧЕНИЕ. Например, если записать так:

Awk " /ADDR/ { if(secret==0) print $0 } /* $0 - это полная строка */ /secretstart/ { secret=1} /secretend/ { secret=0} "

и дать следующий ввод

ADDR a normal addr secretstart ADDR a secret addr ADDR another secret addr a third secret ADDR secretend ADDR normal too

то будет напечатан первый "secret" addr. При том, что первоначальный пример скроет оба секрета.

Часть третья: Специальные переменные

Мы уже сказали про обычный синтаксис awk. Сейчас давайте начнём рассматривать модные штуки.

awk имеет «специальные» строки соответствия: "BEGIN " и "END "

Директива BEGIN вызывается однажды перед чтением каких-либо строк из данных, никогда снова.

Директива END вызывается после прочтения всех строк. Если дано несколько файлов, то она вызывается только после завершения самого последнего файла.

Обычно вы будете использовать BEGIN для различной инициализации, а END для подведения итогов или очистки.

BEGIN { maxerrors=3 ; logfile=/var/log/something ; tmpfile=/tmp/blah} ... { blah blah blah } /^header/ { headercount += 1 } END { printf("всего подсчитано заголовков=%d\n", headercount);

Этот пример посчитает количество раз, которое встречается "header" в файле ввода и напечатает общее количество только после завершения обработки всего файла.

AWK также имеет множество других специальных величин, которые вы можете использовать в секции { }. Например,

Print NF

даст вам общее количество колонок (Number of Fields - Количество полей) в текущей строке. FILENAME будет текущим именем файла, подразумевается, что имя файла было передано в awk, а не использована труба.

Вы НЕ МОЖЕТЕ ИЗМЕНИТЬ NF самостоятельно.

Аналогично с переменной NR , которая говорит, как много строк вы обработали. ("Number of Records" - Количество записей)

Есть и другие специальные переменные, вы даже такие, которые вы МОЖЕТЕ изменить в середине программы.

Часть четвёртая: Простые примеры Awk

Чтобы проиллюстрировать и закрепить сказанное, давайте рассмотрим несколько конкретных примеров. Для них нам понадобятся три небольших текстовых файла.

Для последующих примеров давайте создадим файл field_data.txt со следующим содержимым:

Roses are red, Violets are blue, Sugar is sweet, And so are you.

Echo -e "Roses are red,\nViolets are blue,\nSugar is sweet,\nAnd so are you." > field_data.txt

Создадим файл letters.txt следующего содержания

A bb ccc dddd ggg hh i

В командной строке это можно сделать так:

Echo -e "a\nbb\nccc\ndddd\nggg\nhh\ni" > letters.txt

И, наконец, создадим файл mail-data со следующим содержимым:

Amelia 555-5553 [email protected] F Anthony 555-3412 [email protected] A Becky 555-7685 [email protected] A Bill 555-1675 [email protected] A Broderick 555-0542 [email protected] R Camilla 555-2912 [email protected] R Fabius 555-1234 [email protected] F Julie 555-6699 [email protected] F Martin 555-6480 [email protected] A Samuel 555-3430 [email protected] A Jean-Paul 555-2127 [email protected] R

Это можно сделать в командной строке так:

Wget https://raw.githubusercontent.com/tdhopper/awk-lessons/master/data/mail-data -O mail-data

Простой паттерн (образец)

Если нам нужны строки длиннее, чем два символа, и мы хотим использовать действие по умолчанию (print ), то мы получаем:

Awk "length $0 > 2" letters.txt bb ccc dddd ggg hh

$0 - это встроенная переменная, которая содержит строку.

Простая функция

Если мы не указываем образец, то соответствовать будет каждая строка. Тривиальным действием будет напечатать каждую строку:

Awk "{ print }" letters.txt a bb ccc dddd ggg hh i

Используя функцию length в качестве нашего действия, мы можем получить длину каждой строки:

Awk "{ print length }" letters.txt 1 2 3 4 3 2 1

Это действие применяется безоговорочно к целой строке. Мы также можем указать это явно:

Awk "{ print length $0 }" letters.txt 1a 2bb 3ccc 4dddd 3ggg 2hh 1i

Awk имеет специальные элементы управления для выполнения некоторого кода перед началом ввода файла и после его завершения.

Awk "BEGIN { print "HI" } { print $0 } END { print "BYE!" }" letters.txt HI a bb ccc dddd ggg hh i BYE!

Мы можем иметь больше элементов управления во время печати используя printf .

Awk "BEGIN { printf "%-10s %s\n", "Name", "Number" \ printf "%-10s %s\n", "----", "------" } \ { printf "%-10s %s\n", $1, $2 }" mail-data Name Number ---- ------ Amelia 555-5553 Anthony 555-3412 Becky 555-7685 Bill 555-1675 Broderick 555-0542 Camilla 555-2912 Fabius 555-1234 Julie 555-6699 Martin 555-6480 Samuel 555-3430 Jean-Paul 555-2127

Совмещаем образцы и функции

Конечно, паттерны и функции можно совмещать так, что функция будет применяться только если строка соответствует образцу.

Мы можем напечатать длину всех строк, длиннее 2 символов.

Awk "length($0) > 2 { print length($0) }" letters.txt 3 4 3

На самом деле, мы не обязаны ограничивать Awk только одним паттерном! Мы можем иметь произвольное количество образцов, разграниченных точкой с запятой или новой строкой:

Awk "length($0) > 2 { print "Long: " length($0) }; length($0) < 2 { print "Short: " length($0) }" letters.txt Short: 1 Long: 3 Long: 4 Long: 3 Short: 1

Множество полей

Awk предназначена для простой обработки данных с множеством полей в ряду. Разделитель полей может быть указан ключом -F .

Пример файла, где разделителем является пробел:

Awk "{ print }" field_data.txt Roses are red, Violets are blue, Sugar is sweet, And so are you.

Если мы указываем разделитель полей, мы можем напечатать второе поле каждой строки:

Awk -F " " "{ print $2 }" field_data.txt are are is so

Мы не получим ошибку, если строка не имеет соответствующего поля; нам просто будет показана пустая строка:

Awk -F " " "{ print $4 }" field_data.txt you.

Поскольку по умолчанию разделителем и так является пробел, то предыдущая команда дала бы точно такой же результат и без использования опции -F. Для более осмысленного примера, создадим ещё один файл rates.txt со следующим содержимым:

Pilcrow,Humphrey,3 Pilcrow,Zora,1 Plinius,Oldone,4 Razniecki,Anton,7 Russell,Bertrand,0

Теперь в качестве разделителя укажем , (запятую) и выведем содержимое второго столбца:

Awk -F "," "{ print $2 }" rates.txt Humphrey Zora Oldone Anton Bertrand

Выражение разделителя интерпретируется как регулярное выражение.

Awk -F "((so)?are|is) " "{print "Field 1: " $1 "\nField 2: " $2}" field_data.txt Field 1: Roses Field 2: red, Field 1: Violets Field 2: blue, Field 1: Sugar Field 2: sweet, Field 1: And Field 2: you.

Регулярные выражения

Образцы (паттерны) могут быть регулярными выражениями, а не только встроенными функциями.

Мы можем использовать регулярные выражения для поиска всех слов в мире Unix с 5 гласными подряд.

Awk "/{5}/" /usr/share/dict/words cadiueio Chaouia euouae Guauaenok

Передача переменных в программу

Опция -v для Awk позволяет нам передать переменные в программу. Например, мы можем использовать это для жёстких констант кода.

Awk -v pi=3.1415 "BEGIN { print pi }" 3.1415

Мы также можем использовать -v для передачи переменных Bash как переменных Awk

Awk -v user=$USER "BEGIN { print user }" mial

Выражения If-else

If-else выражения в Awk имеют вид:

If (условие) тело-тогда

Например:

Printf "1\n2\n3\n4" | awk \ "{ \ if ($1 % 2 == 0) print $1, "is even"; \ else print $1, "is odd" \ }" 1 is odd 2 is even 3 is odd 4 is even

Циклы

Awk включает несколько выражений цикла: while , do while и for .

Они имеют ожидаемый синтаксис C.

Awk \ "BEGIN { \ i = 0; \ while (i < 5) { print i; i+=1; } \ }" 0 1 2 3 4 awk \ "BEGIN { \ i = 0; \ do { print i; i+=1; } while(i < 0) \ }" 0 awk \ "BEGIN { \ i = 0; \ for(i = 0; i<5; i++) print i \ }" 0 1 2 3 4

for также может задавать цикл через ключи массива, which будет рассмотрена позже.

Часть пятая: Вызов функций

Следующий компонент AWK - это все его специальные встроенные функции.

AWK имеет функции, которые сделают среднего C программиста весьма счастливым. Здесь такое добро как sin()/cos()/tan(), rand(),index(), sprintf(), tolower(), system()

Функции сгруппированы, их можно рассматривать следующим образом:

Математические

+, -, /, *, sin(), cos(), tan(), atan(), sqrt(), rand(), srand()

Они сами за себя говорят, по крайней мере, мне хочется так думать.

Awk -v pi=3.1415 "BEGIN { print exp(1), log(exp(1)), sqrt(2), sin(pi), cos(pi), atan2(pi, 2) }" 2.71828 1 1.41421 9.26536e-05 -1 1.00387

Программа может сгенерировать случайное число в диапазоне (0, 1).

По умолчанию Awk начинает с одного и того же начала (сида) для Awk. Запуск этой команды два раза подряд возвратит одинаковый результат:

Awk "BEGIN { print rand(); print rand() }" 0.237788 0.291066

Для установки начала (сида) можно использовать функцию srand:

Awk "BEGIN { srand(10); print rand(); print rand() }" 0.255219 0.898883 awk "BEGIN { srand(10); print rand(); print rand() }" 0.255219 0.898883

Функция int возвращает "самое близкое целое число к x, расположенное между x и нулём и с отброшенным ведущим нулём".

Awk "BEGIN { print "int(0.9) = " int(0.9); print "int(-0.9) = " int(-0.9) }" int(0.9) = 0 int(-0.9) = 0

Манипуляция строками

  • index() скажет вам, встречается ли, а если да, то где, строка в подстроке.
  • match() похожая, но работает для регулярных выражений.
  • sprintf() даёт вам способы форматировать вывод и по пути делать преобразования. Это должно быть знакомо всем, кто использовал printf() с C. Например,
newstring=sprintf("one is a number %d, two is a string %s\n", one, two); print newstring

"%d " говорит "напечатай значение, соответствующее мне, в виде десятичного числа"
"%s " говорит "напечатай значение, соответствующее мне, в виде строки"

Т.е. если вы хотите объединить две строки без разрывов, ОДИН из способов будет использование

Newstring=sprintf("%s%s", one, two)

  • length() просто даёт вам простой способ подсчитать количество символов в строке, если вам это понадобится.

Функция substr(s, m, n) возвратит подстроку в n -символов, начинающуюся с позиции m , отсчитанной от 1.

Awk "{ print $1, substr($1, 2, 3) }" field_data.txt Roses ose Violets iol Sugar uga And nd

index(s, t) возвращает `позицию в s в которой встречается строка t , или 0 если она не встречается.`

Паттерн для index не является регулярным выражением.

Awk "{ print $1, index($1, "s") }" field_data.txt Roses 3 Violets 7 Sugar 0 And 0

match(s, r) возвращает позицию в s в которой встречается регулярное выражение r , или 0 если оно не встречается. Переменные RSTART и RLENGTH устанавливаются в позицию и длину совпадающей строки.

match - это как index кроме того, что паттерн является регулярным выражением.

Awk "{ print $1, match($1, "") }" field_data.txt Roses 3 Violets 7 Sugar 1 And 0 # "Поиск трёх или более повторяющихся букв" awk "{ match($1, "{3}"); print $1, "\tpattern start:", RSTART, "\tpattern end:", RLENGTH }" letters.txt a pattern start: 0 pattern end: -1 bb pattern start: 0 pattern end: -1 ccc pattern start: 1 pattern end: 3 dddd pattern start: 1 pattern end: 3 ggg pattern start: 1 pattern end: 3 hh pattern start: 0 pattern end: -1 i pattern start: 0 pattern end: -1

split(s, a, fs) расщепляет строку на массив элементов a, a, …, a, и возвращает n .

Разделение делается по регулярному выражению fs или с разделителем полей FS , если fs не дан. Пустая строка в качестве разделителя полей расщепляет строку в массив элементов посимвольно.

Awk "BEGIN { print split("It-was_the-best_of-times", output_array, "[-_]"), output_array, output_array }" 6 was best

sub(r, t, s) заменяет на t первое вхождение регулярного выражения r в строке s . Если не дана s, то используется $0

s является строкой, в которой происходит замена. Вместо возврата новой строки с произведённой заменой будет возвращено количество сделанных замен (0 или 1).

Awk "BEGIN { s = "It was the best of times, it was the worst of times"; \ print "Num. matches replaced:", sub("times", "gifs", s); \ print s }" Num. matches replaced: 1 It was the best of gifs, it was the worst of times

gsub делает то же самое, что и sub за исключением того, что заменяются все вхождения регулярного выражения; sub и gsub возвращают количество замен.

Awk "BEGIN { s = "It was the best of times, it was the worst of times"; \ print "Num. matches replaced:", gsub("times", "cats", s); \ print s }" Num. matches replaced: 2 It was the best of cats, it was the worst of cats sprintf sprintf(fmt, expr, ...) returns the string resulting from formatting expr ... according to the printf(3) format fmt awk "BEGIN { x = sprintf("[%8.3f]", 3.141592654); print x }" [ 3.142]

Функции уровня системы

system() позволяет вам вызвать потенциально ЛЮБОЙ исполнимый файл в системе. Целевая программа может как быть в вашей $PATH , или вы можете указать её по абсолютному пути.

Например, страшное

System("rm -rf $HOME");

System("/bin/kill 1")

Если вы хотите делать более сложные вещи, вы, вероятно, в конечном итоге делает что-то вроде

Sysstring=sprintf("somecommand %s %s", arg1, arg2); system(sysstring)

close() - это важная функция, которую часто упускают из вида. Вероятно, это из-за того, что нет очевидного вызова open() , поэтому народ не думает про вызов close() . И для большинства целей это и не нужно. Но вы ДОЛЖНЫ ДЕЛАТЬ ЭТО, если вы имеете дело с боее чем одним файлом вывода.

Awk даёт вам возможность открыть произвольный файл на лету. Например

/^file/ { print $3 >> $2 }

должен взять строку "file output here-is-a-word", открыть файл "output" и напечатать "here-is-a-word" в него.

AWK является "умной", в том, что отслеживает, какие файлы вы открывайте и СОХРАНЯЕТ их открытыми. Она предполагает, если вы открыли файл один раз, вы, вероятно, сделать это снова. К сожалению, это означает, что, если вы откроете МНОГО файлов, файловые дескрипторы могут закончиться. Поэтому, когда вы знаете, что закончили с файлом, закройте его. Поэтому для улучшения примера выше, вы должны использовать что-то в духе следующих строк:

/^file/ { if ($2 != oldfile) { close(oldfile) }; print $3 >> $2 ; oldfile = $2; }

Часть шестая: Массивы

Понятие массива

Мы уже рассмотрели переменные как имена, которые содержат значение. Массивы являются продолжением переменных. Массивы - это переменные, которые содержат более одного значения. Они могут содержать более чем одно значение поскольку каждое это значение имеет свой номер.

Если вам нужно иметь три значения, вы можете сказать:

Value1="one"; value2="two"; value3="three";

ИЛИ, вы можете использовать

Values="one"; values="two"; values="three";

Первый пример - это три разных переменных со своими именами (которые различаются на один символ). Второй пример - это массив, который состоит из одной переменной, но содержит много значение, каждое из которых имеет свой номер.

При использовании переменной в качестве массива, всегда нужно помещать значение в квадратные скобки . Вы можете выбрать любое имя для переменной массива, но с этого момента это имя можно использовать ТОЛЬКО для массива. Вы НЕ МОЖЕТЕ осмысленно делать

Values="one"; values="newvalue";

Тем не менее, вы МОЖЕТЕ переназначить величины, как для нормальных переменных. Т.е. следующее ЯВЛЯЕТСЯ правильным:

Values="1"; print values; values="one"; print values;

Интересно то, что в отличие от некоторых других языков, вы не обязаны использовать только номера. В примерах выше ,, на самом деле истолковываются как [“1”], [“2”], [“3”]. Что означает, что вы также можете использовать другие строки в качестве идентификаторов, и рассматривать массив почти как базу данных с одной колонкой. Официальное название для этого «ассоциированный массив».

Numbers["one"]=1; numbers["two"]=2; print numbers["one"]; value="two"; print numbers; value=$1; if(numbers = ""){ print "no such number"; }

Когда и как использовать массивы

Могут быть различные случае, когда вы можете выбрать использование массивов. Некоторые при работе с awk вообще обходятся без массивов. Но это не совсем верная позиция: для массивов существуют специальные переменные, которые, например, показывают его размер (количество значений в массиве), имеются удобные для перебора членов массива конструкции, некоторые функции возвращают значение в виде массива. В любом случае, давайте рассмотрим несколько примеров, которые могут пригодиться.

Сохранение информации для дальнейшего использования

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

/special/{ savedwords=$2; lnum+=1; } END { count=0; while(savedwords != "") { print count,savedwords; count+=1; } }

Вместо простого вывода слов, вы можете использовать секцию END чтобы перед их отображением сделать дополнительную обработку, которая вам может быть нужна.

Если вы хотите присвоить уникальный индекс значениям (для избегания дубликатов), вы вообще можете отсылать к их значения по их собственным строкам. Или, например, сохранить массив с колонкой 3, проиндексированный по соответствующему значению колонки 2.

{ threecol[$2]=$3 } END { for (v in threecol) { print v, threecol[v] } }

Массивы и функция split()

Другая главная причина для использования массивов, если вы хотите делать подполя. Допустим у вас есть строка, которая имеет несколько крупных подразделений и несколько мелких подразделений. Другими словами, Поля верхнего уровня разделяются пробелами, но затем вы получаете более маленькие слова, разделённые двоеточиями.

This is a variable:field:type line There can be multiple:type:values here

В примере выше четвёртое отделённое пробелом поле имеет подполя, разделённые двоеточиями. Теперь, допустим, вы хотите узнать значение второго подполя в четвёртом большом поле. Один из способов сделать это, вызвать две awk связанные трубой:

Awk "{print $4}" | awk -F: "{print $2}"

Другим способом будет на лету изменить значение "FS", которая содержит разделитель полей (судя по всему, это работает не со всеми реализациями awk):

Awk "{ newline=$4; fs=FS; FS=":"; $0=newline; print $2 ; FS=fs; }"

Но вы также можете сделать это с массивами, используя функцию split() следующим образом:

Awk "{ newline=$4; split(newline,subfields,":"); print subfields} "

В этом случае использование массива это самый обычный и, возможно, самый изящный способ сделать это.

Итак, Awk обеспечивает ограниченное количество структур данных. Кроме скалярных и строковых переменных, в язык встроена структура массивных данных. Хотя официально она называется «массивы», эта структура на самом деле является ассоциированным массивом, аналогичной структуре данных dict в Python.

Массивы не нужно инициализировать. Вы можете просто начать присваивать значения. Обратите внимание, что ключами могут быть цифры или строки.

Awk "BEGIN { \ a = 1.1; \ a = 0; \ a["DOG"] = "CAT"; \ print a, a, a["DOG"] \ }" 1.1 0 CAT

Awk не будет печать переменную без индекса:

Awk "BEGIN { \ a["DOG"] = "CAT"; \ print a \ }" awk: cmd. line:3: fatal: attempt to use array `a" in a scalar context

Хотя мы можем сделать цикл по ключу используя for :

Awk "BEGIN { \ a = 1.1; \ a = 0; \ a["DOG"] = "CAT"; \ for(k in a) print(a[k]) \ }" CAT 0 1.1

Часть седьмая: AWK и оболочки (sh/ksh/bash/csh)

Иногда функционала AWK может быть недостаточно. В этом случае можно интегрировать awk в скрипт оболочки. Далее несколько примеров как это можно сделать.

Простой вывод

Иногда хочется использовать awk просто как программу форматирования, и сбрасывать вывод прямо пользователю Следующий скрипт принимает в качества аргумента имя пользователя и использует awk для дампа информации о нём из /etc/passwd.

Примечание : обратите внимание, что в скрипте одинарные кавычки раскрываются (а не являются вложенными) и между двумя раскрытыми парами одинарных кавычек стоит переменная $1 (вторая), которая в данном случае является аргументом скрипта, в то время как $1 является частью синтаксиса $1 (означает первое поле в строке).

#!/bin/sh while [ "$1" != "" ] ; do awk -F: "$1 == ""$1"" { print $1,$3} " /etc/passwd shift done

Присвоение переменным оболочки вывода awk

Иногда мы хотим использовать awk просто для быстрого способа установить значение переменной. Используя тему passwd, у нас есть способ узнать шелл для пользователя и увидеть, входит ли он в список официальных оболочек.

И опять, обратите внимание, как происходит закрытие одинарных кавычек в выражении awk, После закрытой (второй) кавычки, $1 является переменной, в которую передано значение первого аргумента скрипта, а не частью синтаксиса awk.

#!/bin/sh user="$1" if [ "$user" == "" ] ; then echo ERROR: need a username ; exit ; fi usershell=`awk -F: "$1 == ""$1"" { print $7} " /etc/passwd` grep -l $usershell /etc/shells if [ $? -ne 0 ] ; then echo ERROR: shell $usershell for user $user not in /etc/shells fi

Другие альтернативы:

# Смотрите "man regex" usershell=`awk -F: "/^"$1":/ { print $7} " /etc/passwd` echo $usershell; # Только современные awk принимают -v. Вам может понадобиться использовать "nawk" или "gawk" usershell2=`awk -F: -v user=$1 "$1 == user { print $7} " /etc/passwd` echo $usershell2;

Объяснение дополнительных вышеприведённых методов остаётся домашним заданием читателю 🙂

Передача данных в awk по трубе

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

#!/bin/sh grep -h " /index.html" $* | awk -F\" "{print $4}" | sort -u

  1. Занимательная статья, хочу поблагодарить за труды.

    Нашёл в ней не точность. Если выполнить строку из примера

    Awk -F " " "{ print $2 }" field_data.txt

    она выведет тоже самое, что и

    Awk "{ print $2 }" field_data.txt

    Получается пример с -F не удачно описан.

В этой статье мы покажем вам некоторые практические примеры того, как использовать AWK на .

Введение

AWK назван в честь фамилии его авторов: Альфред Ахо, Питер Вайнбергером и Брайан Керниган. AWK очень полезный язык сценариев для обработки текста. Этот язык выполняется в интерпретаторе. Это позволяет пользователю обрабатывать некоторые входные, определять переменные, использовать логические операторы, строки и числовые функции, извлечения данных и создания отформатированных отчетов. Синтаксис AWK очень близок с языку C и является прямым предшественником Perl. Все сценарии AWK могут быть преобразованы в сценарии Perl с использованием утилиты A2P.

Предпосылки

Интерпретатор AWK является стандартным инструментом, найденным на каждом дистрибутиве Linux. Пакет gawk содержит версию AWK с открытым исходным кодом, и в зависимости от дистрибутива Linux он может быть установлен из исходного файла или с помощью пакетов gawk или mawk, включенных в конкретный дистрибутив Linux.

Установка

С правами суперпользователя

Ssh root@IP_Address

Для того, чтобы установить утилиту командной строки AWK на /Fedora или на любую другую на основе RPM распределения Linux, выполните следующую команду:

Yum install gawk

В / , вам нужно вызвать эту команду, чтобы установить Gawk:

Apt-get install gawk

Примеры команды AWK

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

Вы можете использовать команду AWK для печати только определенных столбцов из поля ввода. Например, с помощью команды приведенной ниже вы можете узнать список IP-адресов, которые подключены к серверу:

Netstat -anp|grep tcp|awk "{print $5}"| cut -d: -f1 | sort | uniq -c | sort -n

Это очень полезно, если вы расследуете, находиться ли ваш сервер под атакой DoS или DDoS.

В следующем примере мы используем AWK для поиска конкретного шаблона в определенных столбцах и делаем какое-то действие, на основе результата:

Exim -bpr | grep frozen | awk {"print $3"} | xargs exim -Mrm

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

AWK часто используется для выполнения полезной и практической обработки и манипуляции текста. Например, мы можем использовать AWK для удаления дубликатов в текстовом файле без сортировки:

Awk "!x[$0]++" file-with-duplicates > new-file-without-duplicates

Следующая команда напечатает пять случайных чисел от 0 до 999:

Awk "BEGIN { for (i = 1; i <= 5; i++) print int(1000 * rand()) }"

Используйте следующую команду, чтобы подсчитать количество строк в файле с именем «sample_file»:

Awk "END { print NR }" sample_file

Следующая команда выведет все строки в файле «sample_file», которые содержат строки, начинающиеся с ‘ A ‘ или ‘a’, за которыми следует ‘ re’:

Awk "/re/{print}" /opt/sample_file

Вы можете использовать команду AWK для более сложных операций. Если ваш веб-сайт работает довольно медленно, вы можете использовать следующую команду, чтобы проверить, есть ли какая-то проблема с диском I/O (и/или сети, в некоторых редких случаях):

Tac /proc/stat | awk "/^btime/ {up=systime()-$2;print "up " up/86400 "d"}; /^cpu / {print "user " $2/up "%, nice " $3/up "%, sys " $4/up "%, idle " $5/up "%, iowait " $6/up "%, steal " $9/up "%\niowait/used " $6 / ($2+$3+$4) ", steal/used " $9 / ($2+$3+$4) }"

IOWAIT означает, как долго процессы блокируются занятые вводом/выводом, в основном дискового хранения или, возможно, сети. STEAL означает, как долго процессы блокируются удачей CPU Time slice на сервере. Выше iowait на время процессора пользователя (=USER + NICE + SYSTEM) показывает занят ввода / вывода, выше украсть просматривается показывает занят CPU.

Следующий сценарий использует простую команду awk, которая выполняет поиск во входном файле ‘/etc/passwd ‘ и предоставляет вывод с именем пользователя, за которым следует дата и время последнего входа:

Vi login-check #!/bin/bash for user in `awk -F: "{print $1}" /etc/passwd` do echo -n "$user: " finger $user | grep Last if [ $? != 0 ]; then echo fi done

Сделайте скрипт исполняемым:

Chmod 755 login-check

Выполните скрипт:

./login-check

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

Вывод

Есть некоторые новые языки, такие как Perl и Python, которые могут быть использованы вместо AWK, но использование AWK имеет ряд преимуществ, так как:

  • AWK очень легко узнать.
  • AWK может быть использован для решения определенных типов задач быстрее и создавать более эффективные сценарии, чем при использовании других инструментов/языков.
  • AWK очень удобен при работе с большими файлами, как журналы и т.д., потому что с помощью команды/скрипа AWK вы можете создать отфильтрованный и удобочитаемый отчет.


Понравилась статья? Поделиться с друзьями: