Записи, поля и пользовательские разделители в awk
Управляйте разделителями входных и выходных полей, чтобы разбивать строки CSV, TSV и журналов на аккуратные столбцы
«Записи, поля и пользовательские разделители в awk» — бесплатный урок DevOps Bootcamp на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения DevOps Bootcamp, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс DevOps Bootcamp содержит 4 уроков всего.
Что такое записи и поля в awk
awk читает входные данные построчно. Каждая строка называется записью, а каждый разделённый пробельными символами фрагмент этой строки — полем.
$0— вся текущая запись (полная строка)$1— первое поле$2— второе поле$NF— последнее поле (NF= количество полей)
По умолчанию awk разделяет поля по любой последовательности пробельных символов (пробелов или табуляций). Благодаря этому утилита сразу подходит для разбора журналов, вывода команд и данных, разделённых пробелами.
#!/usr/bin/env bash
# Show how awk sees records and fields
echo 'alice 30 engineer' | awk '{ print "Name:", $1, "Age:", $2, "Role:", $3 }'
# $NF is always the last field — regardless of how many there are
echo 'one two three four five' | awk '{ print "Last field:", $NF }'Разделитель входных полей: FS
Встроенная переменная FS (разделитель полей) указывает awk, как разделять каждую запись на поля. По умолчанию используется один пробел, который включает режим разделения по пробельным символам.
Задать FS можно двумя способами:
- С помощью флага
-Fв командной строке:awk -F':' - Внутри блока
BEGIN:BEGIN { FS = ":" }
Оба способа эквивалентны. Подход с блоком BEGIN предпочтителен в длинных сценариях, поскольку конфигурация хранится внутри самого сценария, что делает его более читаемым и переносимым.
#!/usr/bin/env bash
# Parse /etc/passwd using ':' as the field separator
# Field 1 = username, field 3 = UID, field 7 = shell
echo 'root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin' \
| awk -F':' '{ print $1, "UID="$3, "shell="$7 }'Задание FS в блоке BEGIN
Для сценариев длиннее однострочной команды стандартной практикой является размещение FS внутри блока BEGIN. Блок BEGIN выполняется до того, как awk прочитает какие-либо входные данные, поэтому разделитель готов уже для первой записи.
Этот шаблон также позволяет задать несколько переменных одновременно, добавить комментарии и сохранить всю логику внутри файла сценария.
Распространённые разделители, которые встречаются на практике:
- Двоеточие
:—/etc/passwd,/etc/shadow - Табуляция
\t— данные TSV, вывод компилятора - Запятая
,— файлы CSV (простые, без полей в кавычках) - Вертикальная черта
|— некоторые форматы журналов, дампы баз данных
#!/usr/bin/env bash
# awk script using BEGIN to set FS for TSV input
printf 'alice\t30\tengineer\nbob\t25\tdesigner\ncarol\t35\tmanager\n' \
| awk 'BEGIN { FS = "\t" } { printf "%-10s age=%-3s role=%s\n", $1, $2, $3 }'Разбор файлов CSV с помощью awk
CSV (значения, разделённые запятыми) — один из самых распространённых форматов данных в разработке для оболочки. Установка FS="," позволяет awk использовать запятые в качестве разделителей.
Важное ограничение: встроенная обработка CSV в awk не учитывает поля в кавычках, содержащие запятые (например, "Smith, John"). Для простых файлов CSV без запятых внутри полей этот способ работает идеально. Для соответствующих RFC 4180 файлов CSV с полями в кавычках используйте полноценный анализатор CSV или miller/csvkit.
Распространённая практическая задача — извлечение определённых столбцов и фильтрация строк по значению. После задания FS awk выполняет обе операции без труда.
#!/usr/bin/env bash
# Simple CSV: extract name and salary columns, filter salary > 50000
data='name,department,salary
alice,engineering,95000
bob,marketing,48000
carol,engineering,112000
dave,hr,45000'
echo "$data" | awk -F',' '
NR == 1 { next } # skip header row
$3 > 50000 { print $1, "earns", $3 }
'Многоประกвольные и регулярные разделители полей
FS в awk не ограничивается одним символом — это может быть регулярное выражение. Это особенно полезно для реальных форматов журналов, где поля разделяются разделителями переменной длины.
Примеры разделителей-регулярных выражений:
FS = "[,;]"— разделение по запятой или точке с запятойFS = "[ \t]+"— разделение по одному или нескольким пробелам или символам табуляции (как по умолчанию, но явно)FS = "::"— разделение по буквальной двойной двоеточииFS = "\\|"— разделение по символу вертикальной черты (его необходимо экранировать)
Если FS является регулярным выражением, awk (gawk) рассматривает его как шаблон, а не как буквальную строку.
#!/usr/bin/env bash
# Log lines with mixed delimiters: split on " | " or "::" or ","
# Here we split on one or more spaces or pipe characters
printf '2024-01-15 | ERROR | disk full | /dev/sda1\n2024-01-15 | INFO | startup ok | main\n' \
| awk -F' \\| ' '{ printf "date=%-12s level=%-6s msg=%s\n", $1, $2, $3 }'
# Split on multiple possible delimiters using character class
echo 'alpha,beta;gamma,delta;epsilon' \
| awk -F'[,;]' '{ for(i=1; i<=NF; i++) print i": "$i }'Разделитель выходных полей: OFS
OFS (разделитель выходных полей) управляет тем, что awk помещает между полями при восстановлении записи с помощью print. По умолчанию используется один пробел.
Главная особенность: OFS вставляется между полями только при использовании синтаксиса с запятыми в print или при присваивании значения полю, после которого awk заново собирает $0. Если в исходном коде Вы объединяете значения пробелами, OFS не используется.
print $1, $2, $3— запятые добавляют OFS между полямиprint $1 " " $2— используется явный пробел, OFS игнорируется
Распространённый шаблон: читать CSV, преобразовывать его и записывать как TSV, задав FS="," и OFS="\t".
#!/usr/bin/env bash
# Convert CSV to TSV using FS and OFS
data='alice,30,engineer
bob,25,designer
carol,35,manager'
echo "$data" | awk 'BEGIN { FS=","; OFS="\t" } { print $1, $2, $3 }'
echo '---'
# OFS also applies when you modify a field — awk rebuilds $0 with OFS
echo 'alice,30,engineer' | awk 'BEGIN { FS=","; OFS=" | " } { $1=$1; print $0 }'Принудительная пересборка $0 с помощью OFS
Есть один тонкий, но важный приём: присваивание любого поля (даже присваивание поля самому себе, $1=$1) заставляет awk заново собрать $0, объединив все поля с помощью OFS.
Это идиоматичный способ изменить разделители записи без ручного вывода каждого поля:
- Задайте
FSкак разделитель входных данных - Задайте
OFSкак нужный разделитель выходных данных - Запустите пересборку с помощью
$1=$1 - Выведите
$0
Этот подход работает с любым количеством полей и избавляет от необходимости жёстко прописывать $1, $2, $3, ....
#!/usr/bin/env bash
# Reformat a pipe-delimited file to comma-separated WITHOUT listing every field
printf 'alice|30|engineer|london\nbob|25|designer|paris\ncarol|35|manager|berlin\n' \
| awk 'BEGIN { FS="|"; OFS="," } { $1=$1; print $0 }'
# Useful when you don't know how many fields there are:
printf 'a|b|c|d|e|f|g\n1|2|3|4|5|6|7\n' \
| awk 'BEGIN { FS="|"; OFS="\t" } { $1=$1; print $0 }'Разделитель записей: RS
Подобно тому как FS разделяет поля внутри записи, RS (разделитель записей) определяет, что отделяет записи друг от друга. По умолчанию RS — это перевод строки, поэтому awk обрабатывает по одной строке за раз.
Изменение RS позволяет выполнять мощную обработку многострочных записей:
RS=""— режим абзацев: пустые строки разделяют записи (поля могут занимать несколько строк)RS=";"— разделение по точкам с запятой (полезно для дампов SQL)RS="\n"— явный перевод строки (значение по умолчанию)
В режиме абзацев (RS="") FS по-прежнему используется для разделения полей внутри многострочной записи, а переводы строк внутри записи также автоматически считаются разделителями полей.
#!/usr/bin/env bash
# Paragraph mode: each blank-line-separated block is one record
# Useful for processing structured text blocks (e.g., stanzas, config sections)
data='Name: Alice
Role: Engineer
City: London
Name: Bob
Role: Designer
City: Paris'
echo "$data" | awk 'BEGIN { RS=""; FS="\n" } {
print "Record", NR":"
for (i=1; i<=NF; i++) print " ", $i
print ""
}'Разделитель выходных записей: ORS
ORS (разделитель выходных записей) выводится после каждого оператора print. По умолчанию это перевод строки (\n), поэтому каждый вызов print выводится с новой строки.
Изменение ORS позволяет:
- Объединять записи в одну строку:
ORS=" " - Добавлять пустые строки между выходными записями:
ORS="\n\n" - Создавать пользовательские форматы вывода, например фрагменты JSON или XML
Обратите внимание, что printf не использует ORS — он применяется только к отдельному оператору print. Используйте printf, когда требуется полный контроль над форматированием.
#!/usr/bin/env bash
# Join all matching lines onto one line by setting ORS to a space
printf 'apple\nbanana\ncherry\ndate\n' | awk '{ ORS=" " } { print $0 }'
echo # final newline
# Add a blank line after every output record for readability
printf 'alice 30 engineer\nbob 25 designer\ncarol 35 manager\n' \
| awk 'BEGIN { ORS="\n\n" } { print $1, "is a", $3 }'Практический пример: разбор журналов доступа Apache
Журналы доступа Apache/nginx имеют хорошо известный формат с полями, разделёнными пробелами. Некоторые поля, например временная метка, содержат пробелы и заключены в квадратные скобки или кавычки, из-за чего непосредственное разделение на поля в awk может быть затруднено.
Практический подход — использовать регулярное выражение в FS, учитывающее структуру данных, либо извлекать определённые поля по их позициям, зная точный формат.
Поля комбинированного формата журналов выглядят примерно так:
- IP клиента
- Идентификатор (обычно
-) - Пользователь, прошедший проверку подлинности (обычно
-) - Временная метка (в квадратных скобках, считается одним токеном)
- Метод+путь+протокол запроса (в кавычках)
- Код состояния HTTP
- Количество байтов в ответе
#!/usr/bin/env bash
# Parse a simulated Apache Combined Log Format line
# Extract: IP, status code, bytes, and request path
logs='192.168.1.10 - alice [15/Jan/2024:10:23:45 +0000] "GET /api/users HTTP/1.1" 200 1523
10.0.0.5 - - [15/Jan/2024:10:23:46 +0000] "POST /login HTTP/1.1" 401 89
172.16.0.3 - bob [15/Jan/2024:10:23:47 +0000] "GET /dashboard HTTP/1.1" 200 8741'
echo "$logs" | awk '{
ip = $1
status = $9
bytes = $10
# Extract the request path from the quoted string in field 7
split($7, parts, "/")
path = "/" parts[2]
printf "ip=%-15s status=%s bytes=%-6s endpoint=%s\n", ip, status, bytes, path
}'Объединение FS, OFS, RS и ORS в конвейере
В реальной разработке shell-скриптов эти разделители редко используются по отдельности. Распространённый шаблон конвейера — использовать awk как конвертер формата в середине конвейера, преобразуя один структурированный формат в другой.
Основные выводы об объединении разделителей:
- Всегда задавайте
FSиOFSвместе вBEGINпри преобразовании форматов - Используйте
$1=$1, чтобы инициировать перестроение$0, когда нужно переформатировать все поля без их перечисления - Изменяйте
RSтолько тогда, когда записи действительно занимают несколько строк - Используйте
ORSдля управления интервалами между выходными записями - Для точного форматирования предпочитайте
printf; используйтеprint, когда удобно автоматическое завершение записей с помощьюORS
#!/usr/bin/env bash
# Full pipeline: read a colon-delimited file,
# filter rows where field 3 (UID) >= 1000 (real users),
# then output as tab-separated: username, UID, home dir
echo 'root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
alice:x:1001:1001:Alice:/home/alice:/bin/bash
bob:x:1002:1002:Bob:/home/bob:/bin/zsh
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin' \
| awk 'BEGIN { FS=":"; OFS="\t" } $3 >= 1000 { print $1, $3, $6 }'Проверка знаний: OFS и перестроение полей
Проверьте, как хорошо Вы понимаете взаимодействие OFS и присваивания полям в awk.
Итоги урока: записи, поля и разделители
Теперь Вы можете полностью управлять тем, как awk читает и записывает структурированные данные. Ниже приведено краткое описание четырёх переменных-разделителей:
FS— разделитель входных полей. Задаётся с помощью-Fили вBEGIN. Может быть символом, строкой или регулярным выражением. Значение по умолчанию: пробельные символы.OFS— разделитель выходных полей. Вставляется между полями при вызовахprint $1, $2и когда awk перестраивает$0после присваивания полю. Значение по умолчанию: один пробел.RS— разделитель входных записей. Определяет, что разделяет записи (строки). Значение по умолчанию: перевод строки. Пустая строка включает режим абзацев.ORS— разделитель выходных записей. Добавляется после каждогоprint. Значение по умолчанию: перевод строки. Его можно изменить, чтобы объединять строки или добавлять интервалы.
Главный шаблон, который следует запомнить: задайте и FS, и OFS в BEGIN, затем используйте $1=$1 для перестроения $0, когда нужно изменить разделители во всех полях без жёсткого задания позиций полей. Этот шаблон работает с файлами с любым количеством столбцов и является основой конвейеров преобразования форматов на базе awk.
Часто задаваемые вопросы
Урок «Записи, поля и пользовательские разделители в awk» бесплатный?
Да — полный текст урока «Записи, поля и пользовательские разделители в awk» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс DevOps Bootcamp, подпишись на CoddyKit PRO. Курс DevOps Bootcamp содержит 4 уроков всего.
Чему я научусь в уроке «Записи, поля и пользовательские разделители в awk»?
Управляйте разделителями входных и выходных полей, чтобы разбивать строки CSV, TSV и журналов на аккуратные столбцы Ты практикуешь DevOps Bootcamp с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать DevOps Bootcamp?
Предыдущий опыт не требуется. DevOps Bootcamp на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Записи, поля и пользовательские разделители в awk»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке DevOps Bootcamp?
Да. Каждый урок DevOps Bootcamp включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Записи, поля и пользовательские разделители в awk
- Шаблоны, диапазоны и блоки BEGIN/END
- Агрегация и группировка с массивами awk
- Функции awk, форматирование printf и создание отчётов