Awk описание. Примеры команды AWK в Linux

02.04.2019

Обзор

События в Delphi

Понимание событий

Обработка сообщений Windows в Delphi

  1. Обзор
  2. Одна из ключевых целей среды визуального программирования - скрыть от пользователя сложность программирования в Windows. При этом, однако, хочется, чтобы такая среда не была упрощена слишком, не до такой степени, что программисты потеряют доступ к самой операционной системе.

    Программирование, ориентированное на события - неотъемлемая черта Windows. Некоторые программные среды для быстрой разработки приложений (RAD) пытаются скрыть от пользователя эту черту совсем, как будто она настолько сложна, что большинство не могут ее понять. Истина заключается в том, что событийное программирование само по себе не так уж сложно. Однако, есть некоторые особенности воплощения данной концепции в Windows, которые в некоторых ситуациях могут вызвать затруднения.

    Delphi предоставляет полный доступ к подструктуре событий, предоставляемой Windows. С другой стороны, Delphi упрощает программирование обработчиков таких событий.

    В данном уроке приводится несколько примеров того, как обрабатывать события в Delphi, дается более детальное объяснение работы системы, ориентированной на события.

  3. События в Delphi
  4. Объекты из библиотеки визуальных компонент (VCL) Delphi, равно как и объекты реального мира, имеют свой набор свойств и свое поведение - набор откликов на события, происходящие с ними. Список событий для данного объекта, на которые он реагирует, можно посмотреть, например, в Инспекторе Объектов на странице событий. (На самом деле, на этой странице представлен список свойств, которые имеют тип вроде TMouseMoveEvent и представляют из себя процедуры-обработчики событий. Существует соглашение по названиям данных свойств. Например, OnDblClick соответствует двойному щелчку мыши, а OnKeyUp - событию, когда нажатая клавиша была отпущена.) Среди набора событий для различных объектов из VCL есть как события, портируемые из Windows (MouseMove, KeyDown), так и события, порождаемые непосредственно в программе (DataChange для TDataSource).

    Поведение объекта определяется тем, какие обработчики и для каких событий он имеет. Создание приложения в Delphi состоит из настройки свойств используемых объектов и создания обработчиков событий.

    Простейшие события, на которые иногда нужно реагировать - это, например, события, связанные с мышкой (они есть практически у всех видимых объектов) или событие Click для кнопки TButton. Предположим, что вы хотите перехватить щелчок левой кнопки мыши на форме. Чтобы сделать это - создайте новый проект, в Инспекторе Объектов выберите страницу событий и сделайте двойной щелчок на правой части для свойства OnClick. Вы получите заготовку для обработчика данного события:

    begin

    end;

    Напишите здесь следующее:

    procedure TForm1.FormClick(Sender: TObject);

    begin

    MessageDlg("Hello", mtInformation, , 0);

    end;

    Каждый раз, когда делается щелчок левой кнопки мыши над формой будет появляться окно диалога (см. рис.1).

    procedure TForm1.FormKeyDown(Sender: TObject;

    var Key: Word;

    Shift: TShiftState);

    begin

    MessageDlg(Chr(Key), mtInformation, , 0);

    end;

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

  5. Понимание событий

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

begin

InitializeMemory;

repeat

CheckForMouseEvent(Events);

CheckForKeyPress(Events)

HandleEvents(Events);

until Done:= True;

DisposeMemory;

end.

Это типичный пример программы, ориентированной на события. Она начинается и заканчивается инициализацией и освобождением памяти. В программе присутствует простой цикл repeat..until, который проверяет появление событий от мыши и клавиатуры и затем дает возможность программисту ответить на эти события.

Переменная Events может быть записью с простой структурой:

TEvent = record

X, Y: Integer;

MouseButton: TButton;

Key: Word;

end;

Тип TButton, указанный выше, можно декларировать так:

TButton = (lButton, rButton);

Эти структуры позволяют вам проследить, где находится мышь, каково состояние ее кнопок, и значение нажатой клавиши на клавиатуре. Конечно, это пример очень простой структуры, но заложенные здесь принципы отражают то, что происходит внутри Windows или внутри других систем, ориентированных на события, вроде Turbo Vision. Если бы программа, приведенная выше, была редактором текста, то обработчик HandleEvent для такой программы мог бы иметь вид:

procedure HandleEvent(Events: TEvent);

begin

case Events.Key of

’A’..’z’: Write(Events.Key);

EnterKey: Write(CarriageReturn);

EscapeKey: Done:= True;

end;

end;

Согласно коду выше, программа будет печатать букву ‘a’ при нажатии этой клавиши и перейдет на новую строку, если нажата клавиша ‘Enter’. Нажатие ‘Esc’ приведет к завершению программы.

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

Надеюсь, что приведенный пример дает некоторое понимание работы ориентированной на события системы. Единственное, что осталось пропущенным - почему система Windows так спроектирована.

Одной из основных причин, почему Microsoft сделал Windows по такой схеме, является тот факт, что множество задач работает в среде одновременно. В многозадачных системах операционная система должна знать, щелкнул ли пользователь мышкой на определенное окно. Если это окно было частично перекрыто другим, то это становится известно операционной системе и она перемещает окно на передний план. Понятно, что неудобно заставлять само окно выполнять эти действия. Операционной системе лучше обрабатывать все нажатия клавиш и кнопок на мыши и затем передавать их в остальные программы в виде событий.

Если кратко, программист в Windows почти никогда не должен напрямую проверять “железо”. Система выполняет эту задачу и передает информацию программе в виде сообщений.

Когда пользователь щелкает мышкой, операционная система обрабатывает это событие и передает его в окно, которое должно обработать данное событие. Созданное сообщение, в этом случае, пересылается в некую процедуру DefWindowProc окна (default window procedure). DefWindowProc - аналог процедуры HandleEvent из примера, приведенного выше.

Каждое окно в Windows имеет свою DefWindowProc. Чтобы полностью понять данное утверждение, представьте, что каждая кнопка, каждый ListBox, каждое поле ввода и т.д. на самом деле являются окнами и имеют свою процедуру DefWindowProc. Это очень гибкая и мощная система, но она может заставить программиста писать очень сложный код. Delphi дает возможность быть защищенным от такой структуры программы.

Почти все, что происходит в Windows принимает форму сообщений и, если вы хотите их использовать в Delphi (в большей мере это необходимо при написании своих собственных компонент), то нужно понять, как эти сообщения работают.

Если посмотреть на DefWindowProc в справочнике по Windows API, то можно увидеть следующее определение:

function DefWindowProc(Wnd: HWnd; Msg, wParam: Word;

lParam: LongInt);

Каждое сообщение, посылаемое в окно, состоит из четырех частей: первая часть - handle окна, получающего сообщение, Msg сообщает, что произошло а третья и четвертая части (wParam и lParam) содержат дополнительную информацию о событии. Вместе эти четыре части являются аналогом показанной выше структуры TEvent.

Вторая часть сообщения имеет длину 16 бит и сообщает, что за событие произошло. Например, если нажата левая кнопка на мыши, то переменная Msg содержит значение WM_LBUTTONDOWN. Существуют десятки различного типа cообщений и они называются вроде WM_GETTEXT, WM_HSCROLL, WM_GETTEXTLENGTH и т.п. Список всех сообщений можно видеть в справочнике по Windows API (on-line help).

Последние две переменные, длиной 16 и 32 бита, называются wParam и lParam. Они сообщают программисту важную дополнительную информацию о каждом событии. Например при нажатии кнопки мыши, lParam содержит координаты указателя мыши.

Одна из хитростей заключается в том, как выделить нужную информацию из этих переменных. В большинстве случаев Delphi освобождает вас от необходимости выполнять данную задачу. Например, в обработчике события OnMouseDown для формы вы просто используете координаты X и Y. Как программисту, вам не нужно прилагать усилия для получения сообщения и связанных с ним параметров. Все, что связано с событиями, представлено в простом и непосредственном виде:

procedure TForm1.FormMouseDown(Sender: TObject;

Button: TMouseButton;

Shift: TShiftState;

X, Y: Integer);

Итак, если подвести итог, то должно стать ясным следующее:

  • Windows является системой ориентированной на события;
  • События в Windows принимают форму сообщений;
  • В недрах VCL Delphi сообщения Windows обрабатываются и преобразуются в более простую для программиста форму;
  • Обработка событий в Delphi сводится к написанию для каждого объекта своих обработчиков;
  • События в программе на Delphi вызываются не только сообщениями Windows, но и внутренними процессами.
      1. Обработка сообщений Windows в Delphi

Конечно, нельзя придумать такую библиотеку объектов, которые бы полностью соответствовали потребностям программистов. Всегда возникнет необходимость дополнения или изменения свойств и поведения объектов. В этом случае, так же, как и при создании своих собственных компонент в Delphi, часто требуется обрабатывать сообщения Windows. Поскольку Object Pascal является развитием и продолжением Borland Pascal 7.0, то это выполняется сходным с BP способом.

Общий синтаксис для декларации обработчика сообщений Windows:

procedure Handler_Name(var Msg: MessageType);

message WM_XXXXX;

Handler_Name обозначает имя метода; Msg - имя передаваемого параметра; MessageType - какой либо тип записи, подходящий для данного сообщения; директива message указывает, что данный метод является обработчиком сообщения; WM_XXXXX - константа или выражение, которое определяет номер обрабатываемого сообщения Windows.

Рассмотрим обработку сообщений на примере. Например, при нажатии правой кнопки мыши на форме в программе появляется всплывающее меню (pop-up menu, если оно было привязано к этой форме). Программист может захотеть привязать к правой кнопке какое-нибудь другое событие. Это можно сделать так:

type

TForm1 = class(TForm)

PopupMenu1: TPopupMenu;

MenuItem1: TMenuItem;

MenuItem2: TMenuItem;

MenuItem3: TMenuItem;

private

{ Private declarations }

procedure WMRButtonDown(var Msg: TWMMouse); message

WM_RBUTTONDOWN;

public

{ Public declarations }

end;

Подчеркнут код, добавленный в декларацию объекта TForm1 вручную. Далее, в секции implementation нужно написать обработчик:

begin

  • , 0);
  • end;

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

    Вообще-то, у класса TForm уже есть унаследованный от дальнего предка обработчик данного события, который называется точно также и вызывает то самое pop-up меню. Если в новом обработчике сообщения нужно выполнить действия, которые производились в старом, то для этого применяется ключевое слово inherited . Если слегка модифицировать наш обработчик, то после диалога будет появляться pop-up меню:

    procedure TForm1.WMRButtonDown(var Msg: TWMMouse);

    begin

    MessageDlg("Right mouse button click.", mtInformation,

  • , 0);
  • inherited;

    end;

    Однако, есть еще способ обработки всех сообщений, которые получает приложение. Для этого используется свойство OnMessage объекта Application, который автоматически создается при запуске программы. Если определен обработчик события OnMessage, то он получает управление при любом событии, сообщение о котором направлено в программу. Следующий код будет приводить к появлению диалога при двойном щелчке мыши на любом объекте в приложении.

    procedure TForm1.FormCreate(Sender: TObject);

    begin

    Application.OnMessage:=AOM;

    end;

    procedure TForm1.AOM(var Msg: TMsg; var Handled: Boolean);

    begin

    Handled:=False;

    if Msg.Message = WM_LBUTTONDBLCLK then begin

    MessageDlg("Double click.", mtInformation, , 0);

    Handled:=True;

    end;

    end;

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

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

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

    В защиту 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, рекомендую обязательно освоить этот скриптовый язык, сложного в нем ничего нет. Каждый пример я планирую сопровождать небольшими комментариями, проливающими свет на нюансы использования тех или иных операторов.
    .

    Ищем строку с параметром bind-address в конфигурационном файле.

    root@debian:~# awk ‘/bind-address/’ /etc/mysql/my.cnf
    bind-address = 127.0.0.1
    bind-address = 192.168.1.110

    Пояснения: AWK имеет следующий синтаксис и опции.

    awk [-f файл_программы | ‘программа’] [-Fразделитель]
    [-v переменная=значение] [файл …]

    −F value — определяет разделитель (устанавливает значение встроенной переменно FS);
    −f file — текст программы считывается из файла, вместо командной линии. Поддерживается чтение из множества файлов;
    −v var=value — присвоение переменной необходимого значения;
    −− — обозначает окончания списка опций.

    Пример №2

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

    root@debian-wordpress:~# cat /etc/mysql/my.cnf | awk ‘/bind-address/’
    bind-address = 127.0.0.1
    bind-address = 192.168.1.110

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

    Пример №3

    Вывести список символьных ссылок и путей к конечным файлам.

    root@debian:~# ls -l /bin/ | awk ‘/lrwxrwxrwx/ { print $9, $10, $11}’
    bzcmp -> bzdiff
    bzegrep -> bzgrep
    bzfgrep -> bzgrep
    bzless -> bzmore
    lessfile -> lesspipe
    lsmod -> kmod
    mt -> /etc/alternatives/mt
    nc -> /etc/alternatives/nc
    netcat -> /etc/alternatives/netcat
    open -> openvt
    pidof -> /sbin/killall5
    rbash -> bash
    rnano -> nano
    sh -> dash
    sh.distrib -> dash

    Пояснения: программа awk представляет из себя пару шаблона (pattern ) и действия ({ action } ), а также определений пользовательских функций. Шаблон и действие имеют следующий вид: шаблон { действие } Шаблон либо действие может быть опущено. В первом случае действие будет выполняться к каждой строке, во втором будет осуществляться обычный вывод на экран, эквивалентный команде { print } . Эти ключевые слова нельзя сочетать с другими шаблонами .

    Входная строка обычно состоит из полей, разделенных пробельными символами. (Эту настройку по умолчанию можно изменить с помощью встроенной переменной FS или опции -F разделитель .) Поля обозначаются $1, $2, …; $0 ссылается на всю строку в целом.

    Пример №4

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

    root@debian:~# awk -F «:» ‘{ print $1 }’ /etc/passwd
    root
    daemon
    bin
    sys
    sync
    games
    man

    (Вывод команды сокращен)

    Пояснения: поскольку в файле /etc/passwd записи хранятся в виде «root:x:0:0:root:/root:/bin/bash «, вполне логично разделителем выбрать двоеточие и вывести самое первое поле ($1 ) каждой строки ($0 ).

    Пример №5

    Все в том же файле с пользователями можно посчитать их количество.

    root@debian:~# awk ‘END { print NR }’ /etc/passwd
    25

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

    В этой статье мы покажем вам некоторые практические примеры того, как использовать 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 вы можете создать отфильтрованный и удобочитаемый отчет.