0Pricing
DevOps Bootcamp · Урок

Записи, поля и пользовательские разделители в 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, учитывающее структуру данных, либо извлекать определённые поля по их позициям, зная точный формат.

Поля комбинированного формата журналов выглядят примерно так:

  1. IP клиента
  2. Идентификатор (обычно -)
  3. Пользователь, прошедший проверку подлинности (обычно -)
  4. Временная метка (в квадратных скобках, считается одним токеном)
  5. Метод+путь+протокол запроса (в кавычках)
  6. Код состояния HTTP
  7. Количество байтов в ответе
#!/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 — локальная установка не требуется.

Все уроки этого курса

  1. Записи, поля и пользовательские разделители в awk
  2. Шаблоны, диапазоны и блоки BEGIN/END
  3. Агрегация и группировка с массивами awk
  4. Функции awk, форматирование printf и создание отчётов
← Назад к DevOps Bootcamp