Использование awk в Linux. Примеры команды AWK в Linux
В этой статье мы покажем вам некоторые практические примеры того, как использовать 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 вы можете создать отфильтрованный и удобочитаемый отчет.
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. Например,
"%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
Занимательная статья, хочу поблагодарить за труды.
Нашёл в ней не точность. Если выполнить строку из примера
Awk -F " " "{ print $2 }" field_data.txt
она выведет тоже самое, что и
Awk "{ print $2 }" field_data.txt
Получается пример с -F не удачно описан.
Текст это сердце Unix. Философия "все есть файл" полностью пронизывает всю систему и разработанные для нее инструменты. Вот почему работа
с текстом является одним из обязательных навыков системного администратора или начинающего пользователя Linux.
AWK - один из самых мощных инструментов для обработки и фильтрации текста, доступный даже для людей никак не связных с программированием.
Использование awk в Linux
Простейшая и часто востребованная задача - выборка полей из стандартного вывода. Вы не найдете более подходящего инструмента для решения этой задачи, чем awk. По умолчанию awk разделяет поля пробелами. Если вы хотите напечатать первое поле, вам нужно просто указать awk параметр $1:
echo "one two three four" | awk "{print $1}"
Да, использование фигурных скобок немного непривычно, но это только в первое время. Вы уже догадались как напечатать второе, третье, четвертое, или другие поля? Правильно это $2, $3, $4 соответственно.
echo "one two three four" | awk "{print $3}"
Иногда необходимо представить данные в определенном формате, например, выбрать несколько слов. AWK легко справляется с группировкой нескольких полей и даже позволяет включать статические данные:
echo "one two three four" | awk "{print $3,$1}"
three one
echo "one two three four" | awk "{print "foo:",$3,"| bar:",$1}"
foo: three | bar: one
Если поля разделены не пробелами, а другим разделителем, просто укажите в параметре -F нужный разделитель в ковычках, например ":" :
echo "one mississippi:two mississippi:three mississippi:four mississippi" | awk -F":" "{print $4}"
four mississippi
Но разделитель не обязательно заключать в ковычки. Следующий вывод аналогичен предыдущему:
echo "one mississippi:two mississippi:three mississippi:four mississippi" | awk -F: "{print $4}"
four mississippi
Иногда нужно обработать данные с неизвестным количеством полей. Если вам нужно выбрать последнее поле можно воспользоваться переменной $NF. Вот так вы можете вывести последнее поле:
echo "one two three four" | awk "{print $NF}"
four
Также вы можете использовать переменную $NF для получения предпоследнего поля:
echo "one two three four" | awk "{print $(NF-1)}"
three
Или поля с середины:
echo "one two three four" | awk "{print $((NF/2)+1)}"
three
echo "one two three four five" | awk "{print $((NF/2)+1)}"
three
Все это можно сделать с помощью таких утилит как sed, cut и grep но это будет намного сложнее.
И еще одна возможность awk, поддержка обработчиков для строк:
echo -e "one 1\n two 2" | awk "{print $1}"
one
two
echo -e "one 1\n two 2" | awk "{print $2}"
1
2
echo -e "one 1\n two 2" | awk "{sum+=$2} END {print sum}"
3
Это означает что мы должны выполнять следующий блок кода для каждой строки. Это можно использовать, например, для подсчета количества переданных данных по запросам из журнала веб-сервера.
Представьте себе, у нас есть журнал доступа, который выглядит так:
23 июля 18:57:12 HTTPD : "GET / Foo / бар HTTP / 1.1" 200 344
23 июля 18:57:13 HTTPD : "GET / HTTP / 1.1" 200 9300
23 июля 19:01:27 HTTPD : "GET / HTTP / 1.1" 200 9300
23 июля 19:01:55 HTTPD : "GET / Foo / Baz HTTP / 1.1" 200 6401
23 июля 19:02:31 HTTPD : "? GET / Foo / Baz страница = 2 HTTP / 1.1" 200 6312
Нам известно что последнее поле это число переданных байт, тогда мы можем использовать переменную $NF:
< requests.log awk "{print $NF}"
344
9300
9300
Основное назначение программы 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
Проверка первого поля на равенство указанной строке и печать второго поля в случае успеха.
04.10.2015
16:55
Утилита awk является примером классического приложения Linux для обработки текста. Она весьма универсальна и эффективна, хоть и не предоставляет полноценный язык программирования. Однако будьте уверены, что ее возможностей вполне хватит для решения многих задач автоматизированной обработки текста (особенно при комбинировании с другими консольными утилитами).
Способы запуска awk-программ
Если awk -программа достаточно простая и короткая, то ее код можно набрать прямо в консоли:
Awk "< код awk-программы >" < имя_файла_для_обработки >
В качестве входных данных для awk можно использовать не только текстовые файлы, но и вывод в стандартный поток других приложений:
< некое_приложение > | awk "< код awk-программы >"
В случае, когда код awk -программы достаточно объемный или должен быть сохранен для повторного использования, его можно вызывать из файла с ключом -f:
Awk -f < имя_файла_с_кодом_awk_программы > < имя_файла_для_обработки >
Для проведения экспериментов используем файл test.cpp , на котором будем проверять результаты работы awk - программ:
#include
Реклама
Фильтрация строк с помощью awk
В первую очередь awk позволяет отбирать строки из текста на основе регулярных выражений и некоторых числовых условий.
Отбор строк, соответствующих регулярному выражению
Например, чтобы получить все строки файла test.cpp , содержащие директиву препроцессора #include , воспользуемся следующей командой:
Awk "/^#\s*include/" test.cpp
Регулярное выражение записывается между двумя символами / . В результате получим:
#include
Отбор строк, НЕ соответствующих регулярному выражению
Чтобы оставить все строки, которые не соответствуют регулярному выражению, воспользуйтесь командой из предыдущего подраздела и поставьте в начало awk -кода восклицательный знак. Например, так мы исключим все закомментированные строки:
Awk "! /^[/]{2}.*/" test.cpp
Вот что осталось:
#include
Отбор строк из заданного диапазона
Определить диапазон строк для вывода на экран можно с помощью двух регулярных выражений, записанных через запятую. В качестве примера найдем определение всех функций, возвращающих int:
Awk "/^int .*(.*) {/, /^}/" test.cpp
Соответствующий результат:
Int main(int argc, char** argv) { std::cout << "Hello, world!" << std::endl; for(int i = 0; i < 10; ++i) { std::cout << i << std::endl; } return 0; } int test2() { std::cout << "Hello, test2!" << std::endl; }
Комбинирование условий фильтрации
Для проверки строк сразу по нескольким условиям используйте операторы && (И) и || (ИЛИ) .
Следующая команда выводит все комментарии, которые не содержат main:
Awk "/[/]{2}.*/ && ! /main/" test.cpp
В итоге имеем:
// Комментарий в стиле С для функции test1() // Комментарий в стиле С для функции test2()
Ранее мы искали диапазон строк по двум регулярным выражениям, но если номера строк, которые нужно вывести, известные заранее, то все упрощается:
Awk "4 < NR && NR < 7" test.cpp
NR - переменная awk , которая определяет номер строки. Таким образом, представленный код выводит 5-ую и 6-ую строки:
Void test1(); int test2();
Отбор строк по условиям относительно отдельных слов
Awk может фильтровать текст не только по строкам, но и по отдельным словам. На i -ое слово в строке можно сослаться с помощью $i . Нумерация начинается с единицы, а $0 определяет содержимое всей строки. Количество слов в строке определяется с помощью переменной NF , поэтому $NF указывает на последнее слово. Например, найдем строки, первым словом которых является int или void:
Awk "$1 == "int" || $1 == "void"" test.cpp
Соответствующий вывод на консоль:
Void test1(); int test2(); int main(int argc, char** argv) { void test1() { int test2() {
Однако проще использовать проверку по регулярному выражению для слова. Для этого в awk предусмотрен специальный оператор ~ , который нужно поставить между переменной, указывающей на слово, и регулярным выражением. В качестве примера перепишем предыдущую команду в более компактном виде:
Awk "$1 ~ /int|void/" test.cpp
Отбор строк на основе числовых характеристик
В awk доступны арифметические операторы языка C, что открывает свободу действий. Пример ниже выводит все четные строки (NR - номер строки):
Awk "NR % 2 == 0" test.cpp
Соответствующий вывод:
#include
Следующая awk -программа выводит все строки, у которых длина 1-ого слова равна трем:
Awk "length($1) == 3" test.cpp
В результате получаем:
Int test2(); int main(int argc, char** argv) { int test2() {
Awk "NF == 2" test.cpp
И соответствующий вывод:
#include
Реклама
Работа со строками в awk
Как вы могли убедиться, awk обладает неплохим набором функций для фильтрации строк текста. Однако для этих строк еще можно выполнять различные преобразования. Команды для работы со строками должны быть обернуты в фигурные скобки { ... } . Код в скобках последовательно вызывается для каждой строки обрабатываемого текста.
Форматированный вывод
В awk имеется прямой аналог функции printf() языка C . В качестве примера выведем в начале каждой строки ее номер:
Awk "{ printf "%-2d %s\n", NR, $0 }" test.cpp
Вот что получили:
1 #include
Функции преобразования
Кроме printf() есть в awk и другие функции. Например, print() и toupper() :
Awk "{ print toupper($0) }" test.cpp
Соответствующий результат:
#INCLUDE
Условные конструкции
В awk -программах доступны операторы if-else . Например, следующий код выводит без изменения строки, у которых на 1-ой позиции стоит int , а на последней - { , иначе на консоль отправляется --- :
Awk " { if($1 == "int" && $NF == "{") print; else print "---" }" test.cpp
Выполнение кода приводит к выводу следующего:
Int main(int argc, char** argv) { --- --- --- --- --- --- --- --- --- --- --- --- --- --- --- int test2() { --- ---
Переменные
Доступны в awk -программах и переменные, которые не требуется предварительно объявлять. Следующий код для подсчета количества строк и слов в тексте поместим в файл stat.awk:
{ lineCount++; wordCount += NF } END { printf "line count: %d, word count: %d\n", lineCount, wordCount }
Тогда его вызов осуществляется следующим образом:
Awk -f stat.awk test.cpp
Результат выполнения:
Line count: 27, word count: 88
Фильтр END указывает, что код в скобках после него должен выполняться только после прохода всех строк. Доступен в awk и фильтр BEGIN , поэтому в более общем случае программа принимает вид:
BEGIN { Вызывается до начала прохода по строкам } { Вызывается для каждой строки после секции BEGIN, но до секции END } END { Вызывается после завершения прохода по строкам }
Wc -lw test.cpp
Циклы
В awk -программах вам также доступны циклы for и while в стиле C . Для примера выведем все строки в обратном порядке. Создадим файл reverse.awk следующего содержимого:
{ for(i = NF; i > 0; --i) printf "%s ", $i; printf "\n" }
Вызовем программу следующий образом:
Awk -f reverse.awk test.cpp
В результате слова в каждой строке будут выведены в обратном порядке:
Нестандартный разделитель слов
По умолчанию awk в качестве разделителя слов использует пробельные символы, однако такое поведение можно изменить. Для этого воспользуйтесь ключом -F , после которого укажите строку, определяющую разделитель. Например, следующая программа выводит название группы и ее пользователей (если в группе есть пользователи) из файла /etc/group , применяя в качестве разделителя символ двоеточия:
Awk -F":" "{ if($4) printf "%15s: %s\n", $1, $4 }" /etc/group
Комбинирование фильтров и команд печати
Все рассмотренные ранее фильтры можно использовать совместно с командами обработки строк. Достаточно записать ограничения перед фигурными скобками. Ниже представлен пример для вывода первых 9 строк вывода команды ps , содержащей информацию о пользователе, идентификаторе процесса и имени команды:
Ps axu | awk "NR < 10 { print $1, $2, $NF }"
После запуска увидим:
USER PID COMMAND root 1 /sbin/init root 2 root 3 root 5 root 7 root 8 root 9 root 10