> For the complete documentation index, see [llms.txt](https://man.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://man.bsdcn.org/man1/awk.1.md).

# awk(1)

`awk` — 基于模式的扫描和处理语言

## 名称

`awk`

## 概要

`awk [-safe] [-version] [-d[n]] [-F fs | --csv] [-v var=value] [prog | -f progfile] file`

## 描述

`awk` 扫描每个输入 `file`，查找与 `prog` 中字面指定的模式集合，或以 `-f` `progfile` 指定的一个或多个文件中的模式集合匹配的行。每个模式可关联一个动作，当 `file` 的某行匹配该模式时执行相应动作。每行与每个模式-动作语句的模式部分进行匹配；对每个匹配的模式执行关联的动作。文件名 ‘-’ 表示标准输入。任何 `var`=`value` 形式的 `file` 被视为赋值而非文件名，并在如果是文件名时本应被打开的时刻执行。

选项如下：

**`-d`** \[`n`] 调试模式。将调试级别设置为 `n`，未指定 `n` 时设为 1。大于 1 的值会导致 `awk` 在致命错误时转储核心。

**`-F`** `fs` 将输入字段分隔符定义为正则表达式 `fs`。

**`--csv`** 使 `awk` 使用（基本符合）标准逗号分隔值（CSV）格式处理记录。

**`-f`** `progfile` 从指定文件 `progfile` 而非命令行读取程序代码。

**`-safe`** 禁用文件输出（`print` >、`print` >>）、进程创建（`cmd |` `getline`、`print |`、`system`）以及对环境的访问（`ENVIRON`；参见下文变量部分）。这是“安全”版 `awk` 的初步（且不太可靠）近似实现。

**`-version`** 将 `awk` 的版本号打印到标准输出并退出。

**`-v`** `var`=`value` 在执行 `prog` 之前将 `value` 赋给变量 `var`；可以出现任意数量的 `-v` 选项。

输入通常由换行符或 `RS` 的值分隔的输入行（记录）组成。如果 `RS` 为空，则任意数量的空行用作记录分隔符，换行符用作字段分隔符（在 `FS` 的值之外）。这在处理多行记录时很方便。

输入行通常由空白或下文所述的扩展正则表达式 `FS` 分隔的字段组成。字段表示为 `$1 , $2 , ...`，而 `$0` 引用整行。如果 `FS` 为空，输入行按每个字符一个字段拆分。虽然 gawk 和 mawk 行为相同，但在 IEEE Std 1003.1-2008 ("POSIX.1") 标准中未明确规定。如果 `FS` 是单个空格，则跳过开头和结尾的空白和换行字符。字段由一个或多个空白或换行字符分隔。空白字符是空格或制表符。如果 `FS` 是除空格外的单个字符，字段由该字符的每次出现分隔。`FS` 变量默认为单个空格。

通常，任意数量的空白分隔字段。要将字段分隔符设置为单个空白，使用 `-F` 选项并指定值 ‘\[\ ]’。如果指定 ‘t’ 为字段分隔符，`awk` 将其视为指定了 ‘et’ 并使用 作为字段分隔符。要将字面 ‘t’ 用作字段分隔符，使用 `-F` 选项并指定值为 ‘\[t]’。

模式-动作语句形式为：

> `pattern` `{` `action` `}`

缺失 `{` `action` `}` 表示打印该行；缺失的模式总是匹配。模式-动作语句由换行符或分号分隔。

在终止语句后、逗号（‘,’）、左花括号（‘{’）、逻辑与（‘&&’）、逻辑或（‘||’）之后，‘do’ 或 ‘else’ 关键字之后，或 ‘if’、‘for’ 或 ‘while’ 语句的右括号之后，允许换行。此外，反斜杠（‘e’）可用于转义标记之间的换行。

动作是语句序列。语句可以是以下之一：

**`if`** `( expression`) `statement` \[`else` `statement`]

**`while`** `( expression`) `statement`

**`for`** `( expression ; expression ; expression ) statement`

**`for`** `( var` `in` `array ) statement`

**`do`** `statement` `while` `( expression`)

**`break`**

**`continue`**

`{` \[`statement ...`] `}`

`expression` # 常用形式 `var` = `expression`

`print` \[`expression-list`] \[>`expression`]

`printf` `format` \[`... , expression-list`] \[>`expression`]

**`return`** \[`expression`]

`next` # 跳过此输入行上的剩余模式

`nextfile` # 跳过此文件剩余部分，打开下一个文件，从顶部开始

`delete` `array` `[` `expression` `]` # 删除数组元素

`delete` `array` # 删除数组的所有元素

`exit` \[`expression`] # 立即退出；状态为 `expression`

语句以分号、换行或右花括号终止。空的 `expression-list` 代表 `$0`。字符串常量用 `""` 引起，其中识别常用的 C 转义序列（完整列表参见 printf(1)）。表达式根据情况取字符串或数值，并使用运算符 `+ - * / % ^`（指数）和连接（由空白指示）构建。运算符 `! ++ -- += -= *= /= %= ^=` `> >= < <= == != ?:` 也可用于表达式中。变量可以是标量、数组元素（表示为 `x[i]`）或字段。变量初始化为空字符串。数组下标可以是任何字符串，不一定是数字；这允许一种形式的关联存储。允许多个下标，如 `[i,j,k]`；各组成部分由 `SUBSEP` 的值分隔连接（参见下文变量部分）。

`print` 语句将其参数打印到标准输出（如果存在 >`file` 或 >>`file` 则打印到文件，如果存在 |\`cmd `则打印到管道），由当前输出字段分隔符分隔，由输出记录分隔符终止。`file`和`cmd `可以是字面名称或带括号的表达式；不同语句中相同的字符串值表示同一个打开的文件。`printf\` 语句根据格式格式化其表达式列表（参见 printf(1)）。

模式是正则表达式和关系表达式的任意布尔组合（使用 `! || &&`）。`awk` 支持扩展正则表达式（ERE）。有关正则表达式的更多信息，参见 re\_format(7)。模式中孤立的正则表达式应用于整行。正则表达式也可出现在关系表达式中，使用运算符 `~` 和 `!~`。/`re`/ 是常量正则表达式；任何字符串（常量或变量）都可用作正则表达式，但不能用作模式中孤立正则表达式的位置。

模式可由两个用逗号分隔的模式组成；在这种情况下，对从第一个模式出现到第二个模式出现（含）之间的所有行执行动作。

关系表达式是以下之一：

**`expression matchop regular-expression`**

**`expression relop expression`**

**`expression`** `in` `array-name`

`(` `expr , expr , ...``) in` `array-name`

其中 `relop` 是 C 中的六个关系运算符之一，`matchop` 是 `~`（匹配）或 `!~`（不匹配）。条件是算术表达式、关系表达式或它们的布尔组合。

特殊模式 `BEGIN` 和 `END` 可用于在读取第一行输入之前和最后一行之后捕获控制。`BEGIN` 和 `END` 不与其他模式组合。它们可在程序中多次出现，并按 `awk` 读取的顺序执行。

具有特殊含义的变量名：

**`ARGC`** 参数计数，可赋值。

**`ARGV`** 参数数组，可赋值；非空成员被取作文件名。

**`CONVFMT`** 转换数字时的转换格式（默认 "" `%.6g )`）。

**`ENVIRON`** 环境变量数组；下标是名称。

**`FILENAME`** 当前输入文件的名称。

**`FNR`** 当前文件中当前记录的序号。

**`FS`** 用于分隔字段的正则表达式；也可通过选项 `-F` `fs` 设置。

**`NF`** 当前记录中的字段数。`$NF` 可用于获取当前记录中最后一个字段的值。

**`NR`** 当前记录的序号。

**`OFMT`** 数字的输出格式（默认 "" `%.6g )`）。

**`OFS`** 输出字段分隔符（默认为空白）。

**`ORS`** 输出记录分隔符（默认为换行符）。

**`RLENGTH`** 由 `match()` 函数匹配的字符串长度。

**`RS`** 输入记录分隔符（默认为换行符）。如果为空，空行分隔记录。如果超过一个字符，`RS` 被视为正则表达式，记录由匹配该表达式的文本分隔。

**`RSTART`** 由 `match()` 函数匹配的字符串的起始位置。

**`SUBSEP`** 分隔多个下标（默认 034）。

## 函数

awk 语言有多种内置函数：算术、字符串、输入/输出、通用和位运算。

函数可定义（在模式-动作语句的位置）如下：

```sh
function foo(a, b, c) { ...; return x }
```

参数如果是标量则按值传递，如果是数组名则按引用传递；函数可递归调用。参数对函数是局部的；所有其他变量是全局的。因此可通过在函数定义中提供多余参数来创建局部变量。

### 算术函数

**`atan2(y, x)`** 返回 `y`/`x` 的反正切值（弧度）。

**`cos(x)`** 返回 `x` 的余弦值，`x` 以弧度为单位。

**`exp(x)`** 返回 `x` 的指数。

**`int(x)`** 返回 `x` 截断为整数值后的结果。

**`log(x)`** 返回 `x` 的自然对数。

**`rand()`** 返回随机数 `n`，满足 0 ≤ `n` < 1。

**`sin(x)`** 返回 `x` 的正弦值，`x` 以弧度为单位。

**`sqrt(x)`** 返回 `x` 的平方根。

**`srand(expr)`** 将 `rand()` 的种子设置为 `expr` 并返回之前的种子。如果省略 `expr`，则使用当前时间。

### 字符串函数

**`gsub(r, t, s)`** 与 `sub()` 相同，但替换所有出现的正则表达式。`gsub()` 返回替换次数。

**`index(s, t)`** 字符串 `t` 在 `s` 中出现的位置，如果未出现则为 0。

**`length(s)`** `s` 作为字符串的长度，对于数组参数是数组元素数，如果未给定参数则是 `$0` 的长度。

**`match(s, r)`** 正则表达式 `r` 在 `s` 中出现的位置，如果未出现则为 0。变量 `RSTART` 设置为匹配字符串的起始位置（与返回值相同），如果未找到匹配则为零。变量 `RLENGTH` 设置为匹配字符串的长度，如果未找到匹配则为 -1。

**`split(s, a, fs)`** 将字符串 `s` 拆分为数组元素 `a[1] , a[2] , ... , a[n]` 并返回 `n`。拆分使用正则表达式 `fs`，如果未给定 `fs` 则使用字段分隔符 `FS`。空字符串作为字段分隔符时将字符串按每个字符一个数组元素拆分。

**`sprintf(fmt, expr, ...)`** 根据 printf(1) 格式 `fmt` 格式化 `expr , ...` 所得的字符串。

**`sub(r, t, s)`** 用 `t` 替换字符串 `s` 中第一次出现的正则表达式 `r`。如果未给定 `s`，则使用 `$0`。`t` 中的 ‘&’ 在字符串 `s` 中被替换为正则表达式 `r`。字面 & 可通过在前面加两个反斜杠（‘ee’）指定。字面反斜杠可通过在前面加另一个反斜杠（‘ee’）指定。`sub()` 返回替换次数。

**`substr(s, m, n)`** 返回 `s` 中从位置 `m`（从 1 开始计数）开始、最多 `n` 个字符的子串。如果省略 `n`，或 `n` 指定的字符数超过字符串剩余字符数，子串长度受 `s` 长度限制。

**`tolower(str)`** 返回 `str` 的副本，将所有大写字符转换为对应的小写等效字符。

**`toupper(str)`** 返回 `str` 的副本，将所有小写字符转换为对应的大写等效字符。

### 输入/输出和通用函数

**`close(expr)`** 关闭文件或管道 `expr`。`expr` 应与用于打开文件或管道的字符串匹配。

**`cmd |`** `getline` \[`var`] 从 `cmd` 输出管道流中读取一条输入记录。如果省略 `var`，则设置变量 `$0` 和 `NF`。否则设置 `var`。如果流未打开，则打开它。只要流保持打开，后续调用将从流中读取后续记录。流保持打开，直到通过调用 `close()` 显式关闭。`getline` 成功输入返回 1，文件末尾返回 0，错误返回 -1。

**`fflush([expr])`** 刷新文件或管道 `expr` 的所有缓冲输出，如果省略 `expr` 则刷新所有打开的文件或管道。`expr` 应与用于打开文件或管道的字符串匹配。

**`getline`** 将 `$0` 设置为当前输入文件的下一条输入记录。此形式的 `getline` 设置变量 `NF`、`NR` 和 `FNR`。`getline` 成功输入返回 1，文件末尾返回 0，错误返回 -1。

**`getline`** `var` 将 `$0` 设置为变量 `var`。此形式的 `getline` 设置变量 `NR` 和 `FNR`。`getline` 成功输入返回 1，文件末尾返回 0，错误返回 -1。

**`getline`** \[`var`] <`file` 将 `$0` 设置为 `file` 的下一条记录。如果省略 `var`，则设置变量 `$0` 和 `NF`。否则设置 `var`。如果 `file` 未打开，则打开它。只要流保持打开，后续调用将从 `file` 读取后续记录。`file` 保持打开，直到通过调用 `close()` 显式关闭。

**`systime()`** 返回当前日期和时间作为标准的“自纪元以来的秒数”值。

**`strftime(fmt, timestamp)`** 根据 `fmt`（一个 strftime(3) 支持的格式字符串）格式化 `timestamp`（一个自纪元以来的秒数值）。`timestamp` 和 `fmt` 都可省略；如果没有 `timestamp`，使用当前时间；如果没有 `fmt`，使用默认格式 "%a %b %e %H:%M:%S %Z %Y"。

**`system(cmd)`** 执行 `cmd` 并返回其退出状态。错误时为 -1，正常退出时为 `cmd` 的退出状态，被信号杀死时为 256 + `sig`（其中 `sig` 是致命信号的编号），如果发生核心转储则为 512 + `sig`。

### 位运算函数

**`compl(x)`** 返回整数参数 x 的按位补码。

**`and(v1, v2, ...)`** 对所有提供的参数（作为整数）执行按位与。至少需要两个值。

**`or(v1, v2, ...)`** 对所有提供的参数（作为整数）执行按位或。至少需要两个值。

**`xor(v1, v2, ...)`** 对所有提供的参数（作为整数）执行按位异或。至少需要两个值。

**`lshift(x, n)`** 返回整数参数 x 左移 n 位的结果。

**`rshift(x, n)`** 返回整数参数 x 右移 n 位的结果。

## 退出状态

`awk` 实用程序在成功时退出 0，发生错误时退出 >0。

但请注意，`exit` 表达式可修改退出状态。

## 环境变量

如果环境中设置了 `POSIXLY_CORRECT`，则 `awk` 在处理连续反斜杠和 & 字符方面遵循 `sub()` 和 `gsub()` 的 POSIX 规则。

## 实例

打印长度超过 72 个字符的行：

```sh
length($0) > 72
```

以相反顺序打印前两个字段：

```sh
{ print $2, $1 }
```

同上，但输入字段由逗号和/或空格与制表符分隔：

```sh
BEGIN { FS = ",[ et]*|[ et]+" }
      { print $2, $1 }
```

累加第一列，打印总和和平均值：

```sh
{ s += $1 }
END { print "sum is", s, " average is", s/NR }
```

打印 start/stop 对之间的所有行：

```sh
/start/, /stop/
```

模拟 echo(1)：

```sh
BEGIN { # 模拟 echo(1)
        for (i = 1; i < ARGC; i++) printf "%s ", ARGV[i]
        printf "en"
        exit }
```

向标准错误打印错误信息：

```sh
{ print "error!" > "/dev/stderr" }
```

## 参见

[cut(1)](/man1/cut.1.md), lex(1), printf(1), [sed(1)](/man1/sed.1.md), re\_format(7)

> A. V. Aho, B. W. Kernighan, P. J. Weinberger, "The AWK Programming Language", Addison-Wesley, 1988, ISBN 0-201-07981-X.

## 标准

`awk` 实用程序符合 IEEE Std 1003.1-2008 ("POSIX.1") 规范，但 `awk` 不支持 {n,m} 模式匹配。

标志 `-d`、`-safe` 和 `-version` 以及命令 `fflush , compl , and , or`、`xor , lshift , rshift` 是该规范的扩展。

## 历史

`awk` 实用程序首次出现在 Version 7 AT\&T UNIX。

## 缺陷

数字和字符串之间没有显式转换。要强制将表达式视为数字，对其加 0；要强制将其视为字符串，连接 `""`。

函数中变量的作用域规则是拙劣的；语法更糟。

输入预期为 UTF-8 编码。不处理其他多字节字符集。但是，在八位区域设置中，`awk` 将每个输入字节视为单独的字符。

## 不寻常的浮点值

`awk` 设计于 IEEE 754 算术定义非数字（NaN）和无穷大值之前，而所有现代浮点硬件都支持这些值。

由于 `awk` 使用 strtod(3) 和 atof(3) 将字符串值转换为双精度浮点值，现代 C 库也会将以 `inf` 和 `nan` 开头的字符串分别转换为无穷大和 NaN 值。这导致了奇怪的结果，例如：

```sh
echo nancy | awk '{ print $1 + 0 }'
```

打印 "nan" 而不是零。

`awk` 现在遵循 GNU AWK，在尝试将字符串值转换为数字之前对其进行预过滤，规则如下：

**十六进制**值 自 C99 起允许的十六进制值转换为零，与 C99 之前的行为相同。

**NaN**值 两个字符串 "+nan" 和 "-nan"（不区分大小写）转换为 NaN。其他字符串不会转换。（NaN 可以有符号。）

**无穷大**值 两个字符串 "+inf" 和 "-inf"（不区分大小写）分别转换为正无穷和负无穷。其他字符串不会转换。

## 已弃用的行为

One True Awk 接受 `-F` `t` 作为 `-F` `<TAB>` 的同义词，以便更方便地将制表符指定为分隔符。为了与 gawk 和 mawk 等其他 awk 实现更好的兼容性，上游 One True Awk 已弃用此瑕疵。

历史上，`awk` 不接受 "0x" 作为十六进制字符串。但是，由于 One True Awk 使用 strtod 将字符串转换为浮点数，并且 "0x12" 是浮点数的有效十六进制表示，在 FreeBSD 上，自 One True Awk 在 FreeBSD 5.0 中导入以来，`awk` 已接受此表示法作为扩展。为了在不同 awk 实现之间获得更好的兼容性，上游 One True Awk 已恢复历史行为。gawk 和 mawk 已经表现得类似。从 FreeBSD 14.0 开始，`awk` 将不再接受此扩展。

FreeBSD `awk` 多年来将区域设置设置为与其运行环境匹配。这导致模式范围（如 "\[A-Z]"）在某些区域设置中有时匹配小写字符。这种不当行为从未出现在上游 One True Awk 中，并已在 FreeBSD 12.3、FreeBSD 13.1 和 FreeBSD 14.0 中作为错误删除。
