awk 中的记录、字段和自定义分隔符
控制输入和输出字段分隔符,将 CSV、TSV 和日志行切分为整齐的列。
awk 中的记录、字段和自定义分隔符 是 CoddyKit 上的免费 Linux Command Line & Bash Scripting Mastery 课时。 这是第 1 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Linux Command Line & Bash Scripting Mastery 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Linux Command Line & Bash Scripting Mastery 课程共包含 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 }'在 BEGIN 块中设置 FS
对于超过一行命令的脚本,将 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 }'使用 awk 解析 CSV 文件
CSV(逗号分隔值)是 shell 工程中最常见的数据格式之一。设置 FS="," 后,awk 就会将逗号视为分隔符。
重要注意事项:awk 内置的 CSV 处理不会处理包含逗号的带引号字段(例如 "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 }
'多字符和正则表达式字段分隔符
awk 的 FS 不仅限于单个字符,它还可以是正则表达式。对于字段由长度可变的分隔符分隔的实际日志格式,这一功能非常强大。
正则表达式分隔符示例:
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(输出字段分隔符)控制您使用 print 重建记录时,awk 在字段之间放置的内容。它的默认值是单个空格。
关键点在于:只有在 print 中使用逗号语法,或者为字段赋值并由 awk 重建 $0 时,OFS 才会插入字段之间。如果在源代码中使用空格进行拼接,则不会使用 OFS。
print $1, $2, $3——逗号会触发字段之间的 OFSprint $1 " " $2——使用显式空格,忽略 OFS
一种常见模式是:读取 CSV,进行转换,再通过设置 FS="," 和 OFS="\t" 写出 TSV。
#!/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 }'使用 OFS 强制 awk 重建 $0
这里有一个细微但重要的技巧:为任何字段赋值(即使是将字段赋给自身,例如 $1=$1)都会强制 awk 通过使用 OFS 连接所有字段来重建 $0。
这是重新格式化记录分隔符的惯用方法,无需手动打印每个字段:
- 将
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 是 newline,因此 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 作为流水线中间的格式转换器,把一种结构化格式转换为另一种格式。
组合使用分隔符时,请记住以下要点:
- 转换格式时,始终在
BEGIN中同时设置FS和OFS - 如果希望在不逐一列出字段的情况下重新格式化所有字段,请使用
$1=$1触发对$0的重建 - 仅当记录确实跨越多行时,才更改
RS - 使用
ORS控制输出记录之间的间距 - 需要精确格式化时优先使用
printf;在方便使用ORS自动结束输出时使用print
#!/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 与字段重建
请测试您对 awk 中 OFS 如何与字段赋值交互的理解。
课程回顾:记录、字段和分隔符
现在,您已经可以完全控制 awk 如何读取和写入结构化数据。下面总结四个分隔符变量:
FS— 输入字段分隔符。可通过-F或在BEGIN中设置。可以是字符、字符串或正则表达式。默认值:空白字符。OFS— 输出字段分隔符。在print $1, $2调用中插入字段之间,也会在字段赋值后 awk 重建$0时使用。默认值:单个空格。RS— 输入记录分隔符。定义记录(行)的分隔方式。默认值:newline。设置为空字符串可启用段落模式。ORS— 输出记录分隔符。在每个print后追加。默认值:newline。更改它可以连接多行或添加间距。
最重要的模式是:在 BEGIN 中同时设置 FS 和 OFS,然后在需要跨所有字段重新格式化分隔符时使用 $1=$1 重建 $0,而无需硬编码字段位置。此模式适用于包含任意列数的文件,是基于 awk 的格式转换流水线的基础。
常见问题解答
「awk 中的记录、字段和自定义分隔符」课时是免费的吗?
是的 — 「awk 中的记录、字段和自定义分隔符」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Linux Command Line & Bash Scripting Mastery 课程的其余内容,请升级到 CoddyKit PRO。 Linux Command Line & Bash Scripting Mastery 课程共包含 4 节课。
「awk 中的记录、字段和自定义分隔符」这节课中我会学到什么?
控制输入和输出字段分隔符,将 CSV、TSV 和日志行切分为整齐的列。 你通过在浏览器中直接运行的动手代码来练习 Linux Command Line & Bash Scripting Mastery,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Linux Command Line & Bash Scripting Mastery 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Linux Command Line & Bash Scripting Mastery 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 1 节课,共 4 节。
「awk 中的记录、字段和自定义分隔符」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Linux Command Line & Bash Scripting Mastery 课中编写并运行代码吗?
能。每节 Linux Command Line & Bash Scripting Mastery 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。
此课程中的所有课时
- awk 中的记录、字段和自定义分隔符
- 模式、范围与 BEGIN/END 块
- 使用 awk 数组进行聚合与分组
- awk 函数、printf 格式化与报表生成