awk

来自 阿尔派 Linux


此页面比较了 BusyBox 的 awk 实现(使用配置文件)与 gawk(版本 >= 3.1.8)和 FreeBSD 9 的 nawk,后者基于 Bell Labs/Brian Kernighan 2007 年版本的 awk

它并非旨在作为教程,而是作为技术摘要和 “gotchas” 列表(不同实现可能以不同或意外方式表现的地方)。

注意:此摘要是在我们使用 uClibc 时编写的。此处报告的 Alpine 版本 BusyBox awk 的某些行为可能已随切换到 musl 而发生变化。

调用 awk

awk [-F char_or_regex] [-v var=value ...] [--] 'awk script...' [ ARGV[1] ... ARGV[ARGC-1] ]
awk [-F char_or_regex] [-v var=value ...] -f scriptfile ... [--] [ ARGV[1] ... ARGV[ARGC-1] ]

Gawk 使第三种调用模式成为可能,它将 -f scriptfile 选项与命令行上的 'awk script...' 混合使用

gawk [-F char_or_regex] [-v var=value ...] -f scriptfile ... -e 'awk script...' [--] [ ARGV[1] ... ARGV[ARGC-1] ]

Awkenough 是一小组 Awk 实用程序例程和一个 C 存根,可以更轻松地使用 awk shebang 行编写 shell 脚本。(我很快会为此制作一个 Alpine 软件包。)它还允许混合使用 -f scriptfile 和命令行脚本,使用 gawk 提供的相同 -e 选项。在这两种情况下,长格式选项 --source 的作用与 -e 相同。

注释
  • 所有实现都支持 -v FS=expr 中的 \t 等。BusyBox 在 -F expr支持 \t;这可能是一个 bug。您可以使用 BusyBox ash 的 $'\t' 转义来解决此问题。
  • nawkgawk --traditional(但不包括 gawk --posix)将 -F t 解释为 -F '\t'。这是一个为历史兼容性保留的奇怪的特殊情况。
  • scriptfile 可以是 -,表示 stdin
  • ARGV 可以是任何形式,但 awk 只知道如何自动处理形式为以下内容的参数
    • var=unquoted_value:当主循环到达该 ARGV 时,将进行赋值
    • "":将被跳过
    • 文件名
    • -:将使用 stdin
如果没有处理任何文件(也没有 -),则在所有命令行赋值之后将处理 stdin。
  • 独立脚本应如下所示

内容 foo.awk

#!/usr/bin/awk -f # 将接收脚本的 ARGC、ARGV,其中 ARGV[0]=awk BEGIN { ... } ...
或如下所示

内容 bar.awk

#!/usr/bin/runawk -f /usr/share/awkenough/library.awk ...
  • gawk 将任何位于 -- 之前的无法识别的短选项放入 ARGV;其他实现(和 gawk --traditional)会失败/警告
  • 一些 gawk 独有的选项
    • --lint:警告关于不可移植的构造
    • --re-interval:启用 /pat{1,3}/ 正则表达式;某些版本的 gawk 默认启用,而其他版本则不启用
    • --traditional--compat:使 gawk 的行为类似于 nawk
    • --posix:类似于 --traditional --re-interval,但有一些进一步的限制
    • --exec scriptfile:停止选项处理并禁用任何进一步的 -v;旨在用于不信任环境 (cgi) 中脚本的 shebang 行
    • --sandbox:禁用 system("foo")"foo"|getlinegetline <"file"print|"foo"print >"file" 和 gawk 的 extension(obj, func);因此只有 ARGV 中显式指定的本地资源才可访问。此功能仅在最新版本的 gawk 中可用。
  • 当处理 -f path 选项时,如果 path 不包含 /,gawk 将首先在 AWKPATH 环境变量中指定的目录中搜索

Awk 语法

函数定义
  • 函数具有全局作用域,并且可以从语法上先于其定义的位置调用它们。
  • 标量(即非数组)按值传递,数组按引用传递。
  • 函数调用可以是嵌套的或递归的。函数的每次调用都有其自己的本地环境:因此在返回时,调用者提供的标量参数的值应保持不变(数组参数可能已被修改)。
  • 考虑如下定义的函数
    function foo(x,y) {
        ...
        bar()
        return "result"
    }
    
    function bar(z) {
        y += 1
        return
    }
    

    这里变量 xy 是 foo 的本地变量,变量 z 是 bar 的本地变量。所有其他变量引用都是全局的:与 shell 形成对比,在 shell 中,非本地变量引用而是动态的。也就是说,在 shell 中,当 foo 调用 bar 时,bar 将递增 foo 的本地变量 y。另一方面,在 awk 中,bar 始终只递增全局变量 y

    另请注意,函数调用中的参数数量不必与函数定义中的参数数量匹配;如果提供了更多参数,它们将被忽略;如果提供的参数较少(如 foo 调用 bar 的情况),则根据它们在函数中的使用方式,将缺失的变量分配为 "" 或空数组。

    另请注意,return 语句不必给出显式的返回值(它将默认为 "")。return 语句也可以完全省略。

BEGIN 和 END
BEGIN { ... } ...
END { ... } ...
  • 多个 BEGIN 块被合并;多个 END 块也是如此。
  • 如果只有 BEGIN 块(没有主循环或 END 块),并且未使用 getline,则 POSIX 要求 awk 在不读取 stdin 或任何文件操作数的情况下终止。我检查过的实现都遵守这一点,但一些历史实现在这种情况下会丢弃输入。为了可移植性,您可以显式地将 < /dev/null 附加到您知道不应消耗 stdin 的 awk 调用中。
  • 如果存在主循环或 END 块,并且 awk 对 stdin 的处理没有因在其 ARGV 列表中遇到文件而被抑制,则将在执行任何 END 块之前处理 stdin。
  • 如果存在 END 块,但在它们之外调用 exit status,则主循环停止,控制权立即传递到第一个 END 块。如果在 END 块内调用 exit status,或者如果没有 end 块,则脚本以结果 status 终止。
  • POSIX 要求在 END 内部,只要不调用 getline,所有 FILENAME NR FNR NF 都保留其最近的值。我的实现符合要求,并且还保留了最近的字段 $0 ...,但早期版本的 nawk 没有;Darwin 的 awk 可能也没有。
主循环块

这些形式如下

pattern         { action ... }

pattern,pattern { action ... }

其中 action 默认为 print $0pattern 默认为 1(即 true)。

  • 模式 可以采用以下任何形式
    • /regex/
    • 关系
    • pattern && pattern
    • pattern || pattern
    • ! pattern
    • ( pattern )
    • pattern ? pattern : pattern
    第一种形式的解释与关系表达式 $0 ~ /regex/ 相同。
  • 关系表达式 可以采用以下任何形式
    • expr ~ RE
    • expr !~ RE
    • expr eqop expr
    • expr in arrayname
    • (expr, ...) in arrayname
    • expr
    模式 RE 可以是正则表达式字面量,例如 /a[bc]+/,或任何求值为字符串的表达式,例如 "a[bc]+"。请注意,字符串需要额外的 \ 级别:您可以编写 /\w+/"\\w+"。模式 eqop 可以是以下任何一种:== != <= < > >=。裸表达式(如最后一种形式)在表达式求值为 0"" 时被解释为 false,否则为 true。
其他块形式
BEGINFILE { ... }
ENDFILE { ... }

这些仅在(最新版本的)gawk 中可用。

@include "filename"

这仅在某些版本的 gawk 中可用。另请参阅 Aleksey Cheusov 的 Runawk,它使用不同的方法。

语句

行可以在以下任何一项之后断开:\ , { && || ? :(最后两个仅在 BusyBox 和 gawk 中,而不包括 gawk --posix。)


函数调用
func(expr, ...)

始终求值为一个值,因此可以用作表达式,但也可以出现在语句上下文中。(某些 awk(如 nawk)允许任意表达式出现在语句上下文中;但 BusyBox awk 允许。)

调用用户定义的函数时,函数名称和 ( 之间不能有空格。


赋值
lvalue = expr
lvalue += 1 (similarly for -= *= /= %= ^=)
lvalue ++ (similarly for --)
++ lvalue (similarly for --)

lvalue 可以采用以下任何形式

  • var
  • arrayname[expr]
  • arrayname[expr, ...]
  • $expr

与函数调用类似,赋值始终求值为一个值,因此可以用作表达式,但也可以出现在语句上下文中,即使在 nawk 中也是如此。

delete arrayname[expr]
delete arrayname[expr, ...]
delete arrayname

最后一种形式在 gawk --traditional 中不可用。更具可移植性的是,您可以使用以下方法获得相同的效果

split("", arrayname)

删除数组后,它不再包含任何元素;但是,数组名称仍然不可用作标量变量。


控制运算符
if (test) action
if (test) action; else action
if (test) action; else if (test) action
if (test) action; else if (test) action; else action
if (test) { ... }
if (test) { ... } else action

等等。


while (test) action
...
do action while (test)
...

do...while 形式将至少执行一次 action


for (i=1; i<=NF; i++) action
...
for (k in arrayname) action
...

最后一种形式具有未指定的迭代顺序。此外,在迭代数组时添加或删除元素的效果是未定义的。


break
continue

历史悠久的 awk 实现将 while/do/for 循环外部的 breakcontinue 解释为 next。BusyBox 支持此用法;某些版本的 gawk --traditional 也支持。


next

这将读取下一行输入并从第一条规则重新启动主循环。例如,要特殊处理 awk 输入的第一行,您可以这样做

NR==1 {...handle first line...; next }
      {...handle other lines...}

POSIX 未指定 next 在 BEGIN 或 END 块中的行为。


nextfile

这将中止处理当前文件,并从第一条规则开始处理下一个文件(如果有)。它在 gawk --traditional 中不可用。


return [value]

value 默认为 ""。POSIX 未指定函数外部 return 语句的行为。


exit status

这将开始执行 END 规则,如果在 END 块内部调用,则立即退出。status 默认为 0。在 gawk 和 nawk 中,如下序列

    { ... exit n }
END { exit }

将保留 n 状态代码。在 BusyBox awk 中,第二次退出将恢复为默认值 0


打印

对于 printprintf,括号是可选的,但对于 sprintf,括号是强制性的。此外,如果 printprintf 的任何参数包含 >,则应使用括号。

尽管 printprintf 接受带括号的参数列表,但它们的调用是语句而不是表达式。它们不返回值,并且不能出现在非语句上下文中。(另一方面,sprintf 是一个函数;并且它的调用可以出现在语句和表达式上下文中。)

以下第一个

print "a" "b"
print "a","b"

使用单个参数调用 print,该参数是表达式 "a""b" 的简单连接 "ab"。另一方面,第二种形式使用两个参数调用 print。它们将在输出中由 OFS 的当前值分隔---OFS 可能是,但不必是 ""。OFS 的默认值是单个空格。


print

print $0 的解释相同。(另请注意,形式为 pattern 且没有 {...} 的块被解释为具有主体 { print $0 }。)


print ""
print("")

两者都打印换行符。


print (expr, ...) > "path"
print (expr, ...) >> "path"

打印到指定的路径。(另请参阅下面的#内建文件名。)输出文件在执行 close "path" 或 awk 退出之前不会关闭。


print (expr, ...) | "shell pipeline"

打印到指定的管道。管道在执行 close "shell pipeline" 或 awk 退出之前不会关闭。


printf ( format, expr, ... )

可以后跟 > "path">> "path"| "shell pipeline",就像 print 一样。

这通常实现 printf(3) 功能的子集。format 字符串可以包含

  • 普通的 Awk 字符串转义代码,例如
    • \b (0x8),退格一个空格
    • \r (0xd),在 Unix 上退格到当前行的开头
    • \v\f (0xb 和 0xc),在 Unix 上保持在当前列中,但转到下一个屏幕行
    • \n\t\"\\
    • \a (0x7)
    • \c,它忽略字符串的其余部分(我的 awk 实现均未遵守这一点)
    • \000,最多三个八进制数字
    在我尝试过的实现中,只有 gawk 会 printf 或 sprintf "\0";其他实现会将其视为终止字符串。(打印 "\0" 的另一种方法是使用 printf "%c", 0。这适用于 gawk 的 printfsprintf 以及 nawk 的 printf。)
  • 格式化代码,形式如下
    %[n$][#][-|0][+| ]['][minwidth][.precision][wordsize specifier][format specifier]
    

    其中

    • n$ 表示使用 argv[n] 而不是序列中的下一个 argv。计数从 1 开始,只有 gawk 遵守这一点。
    • # 强制八进制数具有初始 0,十六进制数具有初始 0x,浮点数始终具有小数点。
    • - 表示左对齐
    • 0 表示像往常一样右对齐,但用零而不是空格填充
    • + 强制包含符号
    • 如果为负数则以符号开头,如果为正数则以空格开头
    • ' 表示将一百万写成 1,000,000。我的 awk 实现均未遵守这一点。
    • minwidthprecision 可以是 *,在这种情况下,值由下一个 argv 提供。只有 gawk 和 nawk 遵守这一点。
    • precision 给出数字的小数位数,或字符串的最大宽度。
    • wordsize specifier 只有 nawk 遵守这些,并且仅限:hh h l ll
    • format specifier 可以是任何有符号格式 fegdi(后两者等效),或任何无符号格式 xuo,或任何文本格式 sc。在 BusyBox 上,格式 %c 仅处理高达 0x7fff 的值,并且结果始终屏蔽到范围 "\x00"..."\xff"。

    我的 awk 实现均未遵守 %b(类似于 echo -e)或任何其他格式。

表达式

此材料正在进行中...


(上次编辑者:Dubiousjim,于 2024 年 11 月 9 日。)

/pat/ # 当这是一个完整的 expr 时,解释为 `$0 ~ /pat/` 与其他 ERE 的不同之处:{m,n} 被 nawk 和 gawk --traditional 视为字面量 在某些版本的 gawk --traditional 中,[[:classes:]] 不可用。点号和 [^x] 确实匹配换行符 ^ $ 始终锚定,并且仅与缓冲区开始和缓冲区结束匹配(如 \` 和 \')\1 是 \x01;匹配模式中没有反向引用 在 gawk 和 nawk 中,[a\]1] 匹配 a,],1。在 BusyBox 中,它匹配 a,\ 后跟字面量 1,然后是 ]。在 BusyBox 和 gawk 中,/ab\52c/ 被解释为 /ab*c/ 而不是 /ab\*c/。在 nawk 和 gawk --traditional 中,它被解释为后者。/pat/ 中不允许使用字面量换行符,但在解释为模式的字符串中可以。/pat\nmore/ 和 "pat\\nmore" 都可以。x=$1 $2 # 字符串连接 $one $(one+two) # 任何数值表达式都可以跟随 $ x=1,2 # 将 1 赋值给 x x=(1,2) # 将 1 SUBSEP 2 赋值给 x == != <= < > >= ~ !~ && 的优先级高于 || !(k in A) test ? expr : expr 在 BusyBox 和 gawk 中,0[0-7]+ 被解释为八进制,0x[[:xdigit:]]+ 被解释为十六进制 + - * / % ^(通常,在 BusyBox 中,** 也执行求幂)++ -- lvalue | lvalue ++ -- lvalue += ... %= ^= value C 转义序列在 "string" 和 /pat/ 中:"\OOO" 最多 3 位八进制数字 "\\ \" \n \t \r \f \v \b \a" "\xff" # 不在 gawk --posix 中;BusyBox 限制为两位数字 未指定其他 m 的 \m 行为是什么;我的 awk 使用字面量 m(gawk 给出警告)只有 gawk 抑制字符串字面量内部的 \<换行符\> 表达式 `k in array` 不会创建数组条目,但引用 `array[k]` 将创建一个具有未初始化值的条目。(`k in array` 随后将为 true。)另一方面,对 > NF 字段的引用不会创建新字段。对这些字段的赋值会创建它们(增加 NF 并将任何中间字段设置为未初始化值)。对任何字段(甚至 <= NF)的赋值都会导致使用 OFS 重新计算 $0,但不会导致重新解析 $0。(因此,修改/新字段可能包含嵌入的 FS 字符。)对 $0 的赋值确实会导致重新计算 NF、$1、...。

字符串 vs 数值

此材料正在进行中...


(上次编辑者:Dubiousjim,于 2024 年 11 月 9 日。)

未初始化的值包括未设置的变量和数组元素、无效字段(> NF)或长度为 0 的有效字段,以及未分配的函数参数(可以用作标量或数组)。未初始化的标量的值为 "",数学运算符将其视为 0。可以通过 `var ""` 强制进行字符串解释,或通过 `var + 0` 强制进行数值解释。"1text" 被强制转换为 1。在布尔上下文中:当 expr 求值为 0 或 "" 时为 false,否则为 true。$(expr) 不需要转换未初始化或字符串 expr,但我的实现会这样做。“数值字符串” := 值形式为 / *[+-]?NUMBER */,并且它以无法区分字符串和数字的方式提供:n=$1 或 split("string",n[]) 或 getline n n=FILENAME 或 ARGV[1] 或 ENVIRON["foo"] awk -v 'n=...' ... 比较是数值的,当且仅当:两个操作数都是数值,或者一个是数值,另一个是“数值字符串”或未初始化 我的实现不要求至少一个操作数是数值 否则(操作数之一是显式的“字符串”),比较是按字符串进行的。awk 'BEGIN {print "00" == 0}' # false,第一个操作数不是“数值字符串” awk -v'x=00' -v'n=000' 'BEGIN { y="0000" print(x=="0", x==0, x==u, x==n, y=="0", y==0, y==u, y==n) # 只有第二个、第三个和第四个将为 true,所有其他比较都是按字符串进行的 x+y print(x=="0", x==0, x==u, x==n, y=="0", y==0, y==u, y==n) # 在 nawk 中,先前对 y 的算术引用也会将其值转换为“数值字符串” # 因此现在最后三个比较也为 true # 即使是数值常量也容易受到此副作用的影响 #(打印常量,它会被转换为数值字符串,并忽略对 OFMT 的进一步更改)# 但是,对 y 的按字符串评估仍然给出 "0000" }' 在 POSIX 和我的其他实现中,引用没有该副作用。

数组

此材料正在进行中...


(上次编辑者:Dubiousjim,于 2024 年 11 月 9 日。)

flavor[1]="cherry" ... print flavor[1] for (k in flavor) action nfields = split(string, larray, [sep=FS]) # sep 可以是单个字符或正则表达式 delete array[key] # BusyBox 也支持 "delete array" multiarray[k1, k2]="foo" (k1,k2) in multiarray for (kk in multiarray) { split(kk,k,SUBSEP) ... } # 现在 k[1],k[2]

内建函数

此材料正在进行中...


(上次编辑者:Dubiousjim,于 2024 年 11 月 9 日。)

cos(x)、sin(x)、atan2(x,y)、exp(x)、log(x)、sqrt(x) int(x) # 仅向 0 截断,使用 `printf "%.0f" ...` 进行四舍五入 0 <= rand() < 1 # 1 + int(rand() * TOPNUM) # 返回 [1..TOPNUM] 范围内的数字 srand(seed=time_of_day) ~~> oldseed # 位运算仅限 gawk 和 BusyBox and(x,y) or(x,y) xor(x,y) lshift(x,count) rshift(x,count) compl(x) # 在 BusyBox 上为 32 位基数 0xffffffff,在 x86_64 gawk 上可能为 53 位 # 时间函数仅限 gawk 和 BusyBox systime() # 自纪元以来的秒数 strftime("format",systime()=current) mktime("YYYY MM DD HH MM SS [dst?=-1]") ~~> 自纪元以来的秒数 # HH 等值可以是非标准的,例如 -1 # 使用本地时区;dst? 1 是,0 否,<0 自动 tolower("string")、toupper("string") sprintf("format", expr...) # 需要括号 # gawk 具有多字节感知能力,因此 length、substr、index 和 match 都按字符而不是字节计数 length(string=$0) # nawk 和 gawk 支持不带 () 的 length # nawk 和 gawk(但不包括 gawk --posix)支持 length(array) gawk 具有 length(A),返回键的计数而不是 maxkey substr("string", 1-based-start, maxlength=until_end) ~~> "substring" index("haystack","needle") ~~> 基于 1 的索引,如果失败则为 0 match(string, /pat/) ~~> 基于 1 的起始索引,如果失败则为 0 # 设置 RSTART 和 RLENGTH(当匹配失败时,它们分别为 0 和 -1)# ... match($0, /pat/) { print substr($0, RSTART, RLENGTH) } ... # 打印 /pat/ 的首次出现 gawk 具有 `match(string,/pat/,groups)`:用 \\0、\\1、... 的文本和位置填充 groups[0,0start,0length,1,1start,1length,...] split("string", larray, sep=FS) ~~> nitems # 分割为 larray[1]...larray[nitems] # 某些版本的 gawk 允许第四个参数:lsep,以接收实际的 FS 值 (lsep[0] [1] lsep[1] ... [nitems] lsep[nitems]) # BusyBox 和 gawk 接受 "" 和 // 作为 sep;使用 // BusyBox 在末尾创建一个额外的空字段 sub(/pat/, "subst", lstring=$0) ~~> 成功则为 1,失败则为 0 gsub(/pat/, "subst", lstring=$0) ~~> nsubs gensub(/pat/, "subst", "g" 或哪个匹配项,string=$0) ~~> newstring # 仅限 gawk 和 BusyBox # 字符串未被修改 # "subst" 不仅遵守 &(与 \\0 相同),还遵守 \\1、\\2 ... system("command") ~~> 退出代码,不捕获输出 # getline 是一个函数,即使它不使用括号 # 所有形式每次评估都读取一行;类似于 "next",但不从第一条规则重新开始 # 成功时返回 1,EOF 时返回 0,错误时返回 -1;文件不会自动关闭 # 在分配给 $0 时更新 NF getline [var] # 在未提供文件时更新 NR 和 NFR getline [var] < "-" # 从 stdin 而不是 ARGV[ARGIND] 读取 # 循环直到我们得到一个名称 while (!name) { printf("输入名称?"); getline name < "-" } getline [var] < "file" "pipe command" | getline [var] close("file" 或 "pipe commnd") ~~> 成功则为 0 # awk 不会在文件、管道、套接字或协进程返回 EOF 时自动关闭它们。# close() 的返回值未指定;gawk 使用 fclose(3) 或 pclose(3) 的值,如果命名的文件、管道或协进程不是通过重定向打开的,则返回 -1。{ print ... | "sort>tmpfile" } END { close("sort>tmpfile"); while ((getline < "tmpfile")>0) ... close("tmpfile") } fflush(output "file" 或 "pipe command") ~~> 如果所有请求的缓冲区都成功刷新,则为 0,否则为 -1 # 不在 gawk --posix 中 # 如果未提供参数,则刷新 stdout # 如果提供 "",则刷新所有打开的输出文件和管道

内建文件名

只有 gawk 在内部处理这些文件名

  • "/dev/tty"
  • "/dev/stdin"
  • "/dev/stdout"
  • "/dev/stderr"
  • "/dev/fd/n"

但在许多 Unix 系统中,它们在外部仍然可用。作为 getline < "/dev/stdin" 的替代方案,您也可以使用 getline < "-"。作为 print ... > "/dev/stderr" 的替代方案,您也可以使用:print ... | "cat 1>&2"

内建变量

此材料正在进行中...


(上次编辑者:Dubiousjim,于 2024 年 11 月 9 日。)

ENVIRON
这是一个关联数组,保存当前环境变量。该数组是可变的,但 POSIX 未指定对其进行的更改是否必须对从 awk 生成的子进程可见。(在我检查过的所有实现中,此类更改均不可见。)
ARGC 和 ARGV
awk 的命令行参数的数量,以及保存它们的数组。ARGV[0] 通常为 “awk”,ARGV[1]...ARGV[ARGC-1] 将是参数。Awk 特殊处理空参数,以及形式为 var=value 的参数。
如果您的脚本仅具有 BEGIN 块,而没有主循环规则或 END 块,则 awk 不会尝试以任何方式自动读取或处理 ARGV 参数或 stdin。
您可以递减 ARGC;然后 awk 将不会处理任何已丢失的参数。您还可以设置 ARGV[n] = "",或者,也可以 delete ARGV[n]。同样,您可以添加其他 ARGV 条目,但如果您这样做,则需要手动递增 ARGC。
awk 处理的参数不应引用目录或不存在的文件(除非您要手动扫描 ARGV 列表并删除此类参数)。某些 awk 实现的某些版本在被要求处理目录时会发出警告;其他版本会引发错误。
ARGIND
指示 awk 上次处理的 ARGV 条目。这仅在 gawk 和 BusyBox awk 中存在。该变量是可变的;但对其进行的更改在 gawk 中没有副作用。在 BusyBox 中,对其进行的更改会影响在当前条目完成后接下来将处理哪个 ARGV 条目。
FILENAME
指示当前正在处理的 ARGV 条目的名称。当正在处理 stdin 时,这将是 "-"




ERRNO 在 gawk 和 BusyBox 中,当 getline 或 close 失败后包含字符串错误消息。 NR 当前记录号。 NR 是可变的,但更改它不会产生任何副作用。 FNR 类似于 NR,但相对于当前文件。 RS 记录分隔符(默认为换行符)。 如果 RS 为 "",则将其解释为空行跨度,并且 FS 将始终隐式包含 "\n"。 nawk 仅识别 RS 的第一个字符。 BusyBox 和 gawk 允许 RS 为正则表达式,并将 RT 设置为实际的终止文本(在 EOF 时将为 ""): awk -v 'RS=[\t\n]' 'BEGIN {ORS=""} {print $0 RT} END {print "\n"}' awk -v 'RS=oldpat' -v 'ORS=newtext' '{ if (RT=="") printf "%s", $0 # EOF else print }' # 打印匹配项之间的文本 gawk --traditional 仍然识别正则表达式 RS,但不设置 RT。 BusyBox 允许 FS 具有 0 长度的匹配项: echo aaba|awk -F 'b*' '{print NF}' # 产生 5。 ORS 输出记录分隔符(默认为换行符)。 NF 当前记录中的字段数。 当 NF 增加或减少时(在后一种情况下,字段将被丢弃),gawk 和 BusyBox 会重新计算 $0; nawk 两者都不做。 FS 字段分隔符(默认为空格)。 POSIX 要求为 FS 分配新值对当前输入行没有影响; 它仅影响下一个输入行。 只有 gawk 符合; 如果尚未引用任何字段,BusyBox 和 FreeBSD awk 也将对当前行使用新的 FS。 FS=空格(默认):去除前导和尾随空格/制表符,字段由空格/制表符/换行符跨度分隔(在 gawk --posix 中,仅空格/制表符跨度)。 FS=":" 或 "\t":每个字符的出现分隔另一个字段。 FS="pat":分隔符是最左边的最长非空和非重叠的模式匹配项。 FS="" (仅限 gawk 和 BusyBox):按字符拆分。 它们也接受 "" 和 // 作为 split 的参数(后者不能分配为 FS 的值)。 当按 // 拆分时,BusyBox 会生成一个额外的空字段。 我所有的 gawk 都允许 sub(//,...) 和 gsub(//,...):匹配每个字符间的位置,从第一个字符之前开始。 OFS 输出字段分隔符(默认为空格)。 SUBSEP 数组下标分隔符 (\034)。 OFMT 数字的输出格式 (%.6g)。 CONVFMT 数字的字符串转换格式 (%.6g)。 用于比较和数组索引,默认为 OFMT 的值。 整数字符串始终使用 "%d" 转换。 如果 CONVFMT 不是浮点格式说明符,则结果未指定。 RSTART match() 函数匹配的字符串中的第一个位置。 RLENGTH match() 函数匹配的字符串的长度。 IGNORECASE=1 仅在 gawk 和 BusyBox 中可用。 影响所有模式匹配和字符串比较/索引; 不影响数组索引。

仅限 Gawk 的扩展

此材料正在进行中...


(上次编辑者:Dubiousjim,于 2024 年 11 月 9 日。)

某些版本的 gawk 具有: switch (expression) { case value|regex : statement ... [ default: statement ] } 只有 gawk 支持 FIELDWIDTHS="2 4 2" # 使用 FS=FS 恢复为常规字段拆分。 只有某些版本的 gawk 支持 FPAT="满足每个字段的模式" # 使用 FS=FS 恢复为常规字段拆分。 还有 patsplit("string",larray,[pat=FPAT,lsep])。 只有 gawk 具有 PROCINFO 数组,字段包括: version:3.1.8 pid:17171 ppid:17139 pgrpid:17171 uid:0 euid:0 gid:0 egid:0 group1:0 # group1..groupn 是补充组。 FS:FS 或 FIELDWIDTHS 或 FPAT [strftime] [sorted_in]。 只有某些版本的 gawk 允许间接调用函数: ...funcname="foo"; @funcname(1,2)... 只有 gawk 具有: asort(a, [a.backup]) ~~> nelems,字符串 "10" 按字典顺序排序,但数字 10 按算术顺序排序。 asorti(a, [a.backup]) ~~> nelems,这会丢弃原始值并按字典顺序排序原始键。 strtonum("string") # 更具可移植性的方法,使用 "string"+0。 isarray(a) 在 gawk4 中。 command-or-socket |& getline # 协同进程。 print ... |& command-or-socket。 以下特殊文件名可以与 |& 协同进程运算符一起使用,以创建 TCP/IP 网络连接: /inet{,4,6}/tcp/lport/rhost/rport 用于在本地端口 lport 上与远程主机 rhost 的远程端口 rport 建立 TCP/IP 连接的文件。 使用端口 0 让系统选择端口。 使用 /inet4 强制 IPv4 连接,使用 /inet6 强制 IPv6 连接。 普通 /inet 使用系统默认值(最可能是 IPv4)。 /inet{,4,6}/udp/lport/rhost/rport 类似,但使用 UDP/IP 而不是 TCP/IP。 close() 函数的可选第二个参数。 能够将位置说明符与 printf 和 sprintf() 一起使用。 可本地化的字符串。 bindtextdomain()、dcgettext()、dcngettext() 函数。 BINMODE、LINT、TEXTDOMAIN。 使用 extension() 函数动态添加新的内置函数。

有用的链接

这些链接可能也很有趣

Awk 和 Lua

请参阅 Lua 正则表达式摘要

棕色 的命令 来自 awkenough

在 lua 中 在 awk 中
string.find(str, pattern, [startpos]) --> (start,stop) 或 nil match(str, pat) --> 返回并设置 RSTART,也设置 RLENGTH

index(str, needle) --> start 或 0
nthindex(str, needle, [nth=1, permits -1]) --> start 或 0

string.match(str, pattern, [start=1]) --> (%0) 或 nil 或 (%1,%2,...) matchstr(str, pat, [nth=1]) --> \\0,设置 RSTART 和 RLENGTH

gawk 的 match 仅匹配 nth=1; 它返回 RSTART 并用 0,0start,0length,1,1start... 填充第三个数组参数。

string.gmatch(str, pattern) --> 遍历所有匹配项或匹配组集合的迭代器

迭代序列将如下所示: (%0), (%0), (%0) ...; 或者像: (%1,%2...), (%1,%2...),...
使用此函数,无法指定开始匹配的位置。

gmatch(str, pat, MATCHES, STARTS) --> nmatches
string.gsub(str, pattern, replacement, [max#repls]) --> (newstr,nrepls) gensub(pat, repl, nth/"g", str):最接近 Lua 的 gsub

sub(pat, repl, str):变异 str 中的第一个匹配项
gsub(pat, repl, str):变异 str 中的所有匹配项

string:len length(str)
string:lower

string:upper

tolower

toupper

string.rep(str, count,[5.2 adds sep]) rep(str,count,[sep])
string.sub(str, start,[stop]) substr(str,start,[len])

has_prefix
has_suffix

split(str,ITEMS,[seppat],[gawk's SEPS]) --> nitems

gsplit(str,ITEMS,[seppat],[SEPS]) --> nitems
asplit(str, PAIRS, ["="], [" "]) --> nitems

table.concat(tbl,[sep],[start],[stop]) --> string concat([start=1], [len=to_end], [fs=OFS], [A]) --> string

如果要保留现有的 FS,需要使用 gsplit
要连接一个数组而不指定 len: concat(start, uninitialized, OFS, A)

string:reverse reverse([A])
table.remove(tbl, [pos=from end]) --> 原先位于 tbl[pos] 的值 pop([start=from_end], [len=to_end], [A]) --> 由 SUBSEP 分隔的值
table.insert(tbl,[valpos=insert at end],value) --> nil insert(value, [start=after_end], [A]) --> 数组的新长度

extend(VALS, [start=after_end], [A]) --> 数组的新长度

table.sort(tbl, [lessthan]) sort(A)

hsort(A)
qsort(A, 1, length(A))

isempty(A)

has_value(A, value)

includes(A, B, [onlykeys?]):B <= A 吗?

union(A, B, [conflicts]) --> 变异 A
intersect(A, B, [conflicts]) --> 变异 A
subtract(A, B, [conflicts]) --> 变异 A