Shell脚本学习指南-第九章-awk的惊人表现
第9章 awk的惊人表现
本章定位:shell脚本中awk工具完整学习。awk专门做文本处理,POSIX标准工具,各UNIX/Linux都自带;常见实现:
nawk/gawk/mawk。
核心思想:数据驱动编程,自动循环读取输入记录(行),模式匹配成功则执行操作。
9.1 awk命令行
调用语法
两种使用方式:直接写程序片段;把awk代码放到文件用-f读取
| 选项 | 作用 |
|---|---|
-F fs |
字段分隔符,fs是正则表达式;等价修改内建变量FS |
-v var=value |
处理输入文件之前初始化变量,必须写在awk程序代码前面 |
-f file |
从文件读取awk程序,可以多次使用,用于代码库复用 |
-- |
标志:后面全部内容不再解析为选项,全部当做参数/文件名 |
重要规则&示例
- 不写输入文件,则读取标准输入(stdin);特殊文件名
-代表标准输入。 -v必须放在awk程序字符串前面;放在后面会被当成文件名。- 可以在文件名位置赋值变量,文件处理的时候动态改变变量:
awk '{print Pass, $0}' Pass=1 *.tex Pass=2*.tex 解析
拆解这条命令
|
|
先把语法格式重新拆开看:
|
|
'program'这一整块,就是awk程序本体。
{print Pass, $0}全部包裹在单引号' '里面,属于program的内容!,$0属于 awk 语法:代表当前整行记录。
{ }大括号是 awk语言内部语法,不属于shell命令行选项语法。
shell层面:单引号把一整块字符串传给awk,shell不解析里面的大括号。 awk拿到字符串之后,awk解释器才解析
{ print Pass, $0 }。
整行命令分段拆分
按照语法模板:
|
|
- program =
{print Pass, $0} - 后面跟着一堆参数,分两类:
var=value格式:这是运行时变量赋值,不是文件!- 其他:当做输入文件名。
执行顺序(非常关键)
awk处理后面参数是从左到右逐个处理:
- 遇到
Pass=1→ 设置awk变量Pass=1 - 遇到
*.tex→ 把这些tex文件当做输入,读取处理;此时Pass=1生效 - 遇到
Pass=2→ 修改变量Pass=2 - 遇到
*.tex→ 再次读取同样一批tex文件;此时Pass=2生效
效果:同一批文件处理两遍
第一遍:Pass=1,打印1 原始行内容
第二遍:Pass=2,打印2 原始行内容
示例输出:
区分两个完全不同层面,很多人混淆在这里
① Shell命令行层面(操作系统shell,bash/sh)
|
|
awk:shell要执行的外部命令。- 单引号
'xxxx':shell的引号,保护里面所有内容原封不动,完整传给awk程序。
shell完全看不懂
{},{}对shell来说就是普通字符。shell不解析awk语法!
② awk语言层面(awk程序,单引号里面的内容)
{print Pass, $0} 交给awk之后,awk解释器来解析:
{ ... }:awk语法,代表操作action块;Pass:awk变量,不是shell变量;$0:awk内置变量,当前输入的一整行记录。
✔记住分界线:单引号内部全部属于awk语言世界;外面是shell世界。
对比 -v 和 后置 var=value 的区别(重点!)
-v var=value
|
|
‑v 在读取任何输入文件之前就完成赋值,属于初始化,写在awk程序'...'的前面。
- 写在程序后面的
Pass=1 file.txt
|
|
这个赋值发生在处理该参数的时候,是处理文件的过程中动态修改变量。
⚠不能写在'program'前面!只能写在program之后。
❌错误:
✅正确:var=value必须写在 'program' 的后面。
再看语法模板
[-v ...] 在program之前;
[var=value...] 在program之后。
极简小测试,复制运行体会
准备一个简单测试文件 test.txt
执行这条命令:
|
|
输出:
可以看到:文件test.txt被读了两遍,第一遍Pass=1,第二遍Pass=2。
总结
- 语法模板里没有看到
{},但是代码里有'{print Pass, $0}'
{print Pass,$0}全部在shell单引号' '内部,属于模板中的program;{}是awk语言的语法,不是shell命令行选项;shell只负责把整串字符串交给awk。
Pass=1 *.tex Pass=2 *.tex看不懂
- 在awk程序字符串
'...'之后,可以写变量=值,这不是文件名;awk从左到右处理参数; - 先赋值Pass=1,处理tex文件;再赋值Pass=2,再次处理tex文件;
- 效果:同一套文件处理两轮,每轮变量值不一样。
记忆口诀
-v在awk程序前面,全局初始化;
var=value在awk程序后面,处理文件时动态修改;
单引号' '里面:全部归awk管,大括号、$0都是awk语法,shell不碰。
示例:
9.2 awk程序模型
awk核心:
模式(pattern) {操作(action)}
awk自动:读取每一条记录(默认一行),拿记录匹配模式;匹配成功执行大括号里面操作。
4种基础写法
| 写法 | 说明 |
|---|---|
pattern { action } |
模式匹配 → 执行操作 |
pattern |
省略操作,匹配成功,默认打印整条记录($0) |
{ action } |
省略模式,对每一条记录都执行操作 |
| 空 | 什么都不做 |
两个特殊模式:BEGIN / END
BEGIN { ... }读取任何输入文件之前执行,只运行一次。适合做初始化:设置变量、打印表头。END { ... }全部输入处理完成之后执行,只运行一次。适合输出统计汇总报告。
注意:可以写多个BEGIN、END块,按照代码从上到下顺序依次执行。
示例:
9.3 程序元素
awk支持:字符串、数字、变量(标量、关联数组)、表达式、分支、循环、函数。
9.3.1 注释与空白
#行注释,从#到行结尾。- 语句换行:一行放不下,行末尾加
\换行。
9.3.2 字符串与字符串表达式
- 字符串常量使用双引号
"abc";不支持单引号字符串。 - 转义:
\ttab,\n换行;gawk支持\xHH十六进制;POSIX awk只支持八进制\0xx。 - 字符串自动拼接:两个字符串紧挨着写,自动合并,不需要任何运算符。
- 字符串比较运算符:
== != < <= > >= - 正则匹配运算符:
~匹配正则!~不匹配正则
正则两种写法:
/正则/或者"正则字符串",推荐斜杠写法。
9.3.3 数字与数值表达式
awk所有数字全部是双精度浮点数,没有int整数类型。
- 字符串转数字:
s="123"; n=0+s加0触发转换。 - 数字转字符串:
n=123; s="" n拼接空字符串。
运算符优先级(从高到低)
| 运算符 | 说明 |
|---|---|
++ -- |
自增自减(前置/后置) |
^ ** |
指数,右结合 a^b^c → a^(b^c) |
! + - |
逻辑非、一元正负号 |
* / % |
乘、除、取模 |
+ - |
加减 |
< <= > >= == != |
比较 |
&& |
逻辑与(短路) |
| ` | |
?: |
三元运算符 cond ? a : b |
= += -= *= /= %= ^= |
赋值,右结合 |
示例三元运算符:
|
|
内置数学小函数:int() sqrt() sin() cos() log() exp()
|
|
9.3.4 标量变量
- 变量不需要声明,第一次使用自动创建;初始值:空字符串,当做数字时等于0。
- 命名规则:
[A‑Za-z_]开头,后续字母数字下划线;区分大小写a A是两个变量。 - 约定:内置变量全部大写。
awk常用内置标量变量
| 变量 | 含义 |
|---|---|
FS |
输入字段分隔符,默认空格;等价‑F |
OFS |
输出字段分隔符,默认空格 |
RS |
输入记录分隔符,默认换行\n |
ORS |
输出记录分隔符,默认换行\n |
NR |
全局总记录号,所有文件累加行号 |
FNR |
当前文件的记录号,每个文件从1重新计数 |
NF |
当前记录字段数量(一行多少列) |
FILENAME |
当前正在处理的输入文件名 |
9.3.5 数组(关联数组)
awk是关联数组:索引可以是字符串,不强制只能数字。类似hash字典。
遍历数组:
9.3.6 命令行参数 ARGC / ARGV
ARGC:参数总个数ARGV[]数组,保存命令行参数;ARGV[0]是awk程序名字。
⚠
ARGC、ARGV是 awk 内置自带的全局变量(默认就存在,不用你定义),作用:存放 awk 命令行上除了 awk 选项 (-v/-F/-f 等) 之外的所有参数。
命令行参数 ARGC / ARGV 详解
注意:不是shell的$(@/)$argv,是awk自己独立维护的一套参数数组。
1. 哪些参数会进 ARGV?哪些不会?
命令模板:
|
|
会放进ARGV数组:program后面写的所有东西(变量赋值、文件名)
不会进ARGV:-v、-f、-F这类awk命令行选项,以及选项后面跟着的值。
ARGV[0]固定是字符串
awk(程序名),从ARGV[1]开始才是后面的参数。
ARGC = ARGV数组里面元素总数量。
拿这条例子拆解
|
|
-v A=1:选项,不进ARGV-f showargs.awk:选项,不进ARGVfile1.txt、file2.txt:program后面的参数,进入ARGV
所以awk内部:
运行showargs.awk,输出:
2. 再举一个带 var=value 的例子(就是刚才Pass那个)
|
|
'{print Pass,$0}'是program代码,不算参数Pass=1、test.tex、Pass=2、test.tex全部在program之后,全部进入ARGV
ARGV数组:
这就是awk为什么能识别
Pass=1这种后置变量赋值:awk遍历ARGV数组,遇到xxx=yyy格式,就当成变量赋值;其余的当做输入文件名。
3. 关键细节(书里重点)
- ARGV数组可以修改! 你可以在BEGIN代码里删除/修改ARGV的元素,来控制awk读取哪些文件。
很多高级awk脚本靠修改ARGV来动态控制待处理文件列表。
- 区分:
- shell的
$0 $1 $@:是shell拿到的命令行参数,awk看不到。 - awk的
ARGV / ARGC:awk启动之后,自己解析、单独保存的参数列表。两者完全独立。
- shell的
- 什么时候读取ARGV?
ARGV/ARGC在BEGIN块里面就已经准备好了,所以你可以在BEGIN循环打印、修改它们;此时还没有开始读取任何输入文件。
4. 简易对比表
| 参数类型 | 是否进入ARGV |
|---|---|
-v var=val |
❌ |
-f script.awk |
❌ |
-F: |
❌ |
program代码 {print ...} |
❌ |
Pass=1(program后面的变量赋值) |
✅ |
file.txt(program后面文件名) |
✅ |
5. 小思考题
|
|
ARGC等于多少?ARGV[1]、ARGV[2]是什么?
答案 ARGC=3 ARGV[0]=“awk” ARGV[1]=“a.txt” ARGV[2]=“b.txt” -F:是选项,不进ARGV。
记忆要点:选项(-v/-f/-F)被awk提前吃掉,不会进ARGV;只有program之后的所有参数,才会全部扔进ARGV数组。
示例showargs.awk
执行:
|
|
9.4 记录与字段
record记录:默认一行;field字段:一行被FS切分出来的列。
9.4.1 RS 输入记录分隔符
- POSIX awk:RS只能单个字符;默认
\n换行;RS为空代表按段落分割(空行分隔记录) - gawk/mawk扩展:RS支持正则表达式,可以跨行切分记录;配套
RT保存实际匹配到的分隔文本。
9.4.2 FS 字段分隔符
- FS默认值
" "(写的是一个空格,特殊模式!)
⚠这是awk的特殊规则:当FS等于单个空格字符串
" ",不是代表“匹配1个空格” 规则:
- 把一个或者连续多个空格、tab制表符全部当做同一个分隔符;
- 行开头的空白、行末尾的空白直接全部丢掉,不会产生空字段。
测试输入字符串:
|
|
肉眼看:前面3个空格,a,3个空格,b,2个空格,c,后面2个空格。
执行命令:
|
|
输出:
解释:
- 开头一堆空格直接扔掉;
a、b、c之间不管多少连续空格/tab,统一当成1个分隔;- 末尾一堆空格直接扔掉;
- 最后得到3个有效字段:
abc→ 所以NF=3。
日常处理日志、表格,绝大多数场景就用默认FS=" ",非常符合人的直觉。
- FS=
"[ ]"(正则,匹配恰好1个空格字符)
[ ]是正则表达式,方括号里面放一个空格:含义只匹配单个空格。
此时就没有上面那种特殊智能行为!
每遇到1个空格就切一刀;开头、中间、结尾的空格都算数。
同样输入:a b c
字符串分解(·代表1个空格):
···a···b··c··
每碰到一个空格就切一刀:
- 第1个
·切割 → 字段1:空字符串 - 第2个
·切割 → 字段2:空字符串 - 第3个
·切割 → 字段3:a - 第4个
·切割 → 字段4:空字符串 - 第5个
·切割 → 字段5:空字符串 - 第6个
·切割 → 字段6:空字符串 - 第7个
·切割 → 字段7:b - 第8个
·切割 → 字段8:空字符串 - 第9个
·切割 → 字段9:c - 第10个
·切割 → 字段10:空字符串 - 第11个
·切割 → 字段11:空字符串
执行这条完整命令:
输出结果:
可以看到:NF=11,大量空字段!
$1$2是空:来自行最前面的两个空格切出来;$4 $5 $6是空:a和b之间连续多个空格切出来;$10 $11是空:行末尾的空格切出来。
9.4.3 字段 $1 $2 … $NF $0
$0:整条原始记录(整行)$1第一个字段,$2第二个,$NF最后一个字段;NF是字段总数。- 字段可以赋值! 修改字段会触发用
OFS重新拼接$0。
示例:
⚠ 直接打印$0,不修改字段,OFS不会生效。必须至少修改任意一个字段,才会重建$0。
9.5 模式与操作
9.5.1 模式pattern
pattern 位置放的就是一个表达式!表达式计算结果非0 → 条件为真,就执行后面大括号的action。
常用模式示例
| 模式 | 本质表达式 | 含义说明 |
|---|---|---|
NF == 0 |
(NF == 0) |
内置变量NF(当前行字段数)等于0 → 空行,条件成立 |
NR < 5 |
(NR < 5) |
内置变量NR(全局累计行号)小于5,条件成立 |
FNR == 3 |
(FNR == 3) |
内置变量FNR(当前文件内行号)等于3,条件成立 |
$1 ~ /root/ |
($1 ~ /root/) |
字段$1匹配正则/root/,条件成立 |
/error/ |
($0 ~ /error/) |
简写,等价于拿$0匹配正则,条件成立 |
awk规则: 模式位置可以写任意合法awk表达式:可以用内置变量、自定义变量、运算符、正则匹配。
表达式结果:非数字0、非空字符串 → 真;0或者空字符串 → 假。
完整逻辑流程,对每一行输入记录重复执行:
- 读入一行,自动更新内置变量:
$0、$1、$2…、NF、NR、FNR、FILENAME - 计算pattern表达式
NF == 0- 如果这一行是空行:
NF值是0,0 == 0→ 结果为真 → 执行{ print "发现空行" } - 如果不是空行:
NF>0,表达式结果假 → 跳过action,不执行大括号
- 如果这一行是空行:
内置变量会每读完一行自动刷新更新!
所以每处理新一行,NF/NR/FNR就变成这一行对应的数值,拿来做判断。
逗号,是awk专门的范围模式运算符
- 逗号左边:起始条件表达式
- 逗号右边:结束条件表达式
逻辑:
- 一开始状态:不选中
- 一旦左边表达式为真 → 切换为选中状态,执行action
- 后续每一行都执行action,直到右边表达式结果为真;这一行也会执行,然后切换回不选中。
- 之后继续等待下一次左边条件触发。
|
|
FNR==3:表达式,内置变量FNR等于3(每个文件的第3行),开启范围FNR==10:表达式,内置变量FNR等于10(每个文件第10行),关闭范围
所以每个文件,输出3~10行。
⚠重点:FNR是每个文件单独计数,处理下一个新文件的时候FNR自动重置回1。NR是全局,多个文件持续累加不会重置。
NR 和 FNR 对比小例子
假设有两个文件: fileA(2行),fileB(2行)
|
|
输出
- NR:一直往上累加:1,2,3,4
- FNR:打开新文件就重置为1:fileA是1‑2;fileB重新从1‑2
所以:
NR<5:所有文件合起来,总的前4行FNR==3:每一个单独文件的第3行
重要提醒:BEGIN / END块里面这些变量是无效
注意BEGIN块内部:
$0、NF、NR、FNR、FILENAME未定义;END块$0不一定可靠。
为什么?
BEGIN { ... }:还没有读取任何输入行! 一行都没读,没有记录,所以$0、NF、NR、FNR没有有效数据。
- END块:全部行读完。
NR/FNR/NF保留最后一行的值;但是$0部分awk实现会清空,不要依赖。
只有在处理输入记录的时候(也就是BEGIN之外,每一行循环的时候),NF、NR、FNR才会被自动填充有效数值。
9.5.2 操作 action
print输出;print expr1,expr2 多个表达式用逗号隔开,输出用OFS分隔,末尾追加ORS换行。
简写小技巧:1 作为pattern,代表条件永远为真,默认执行print
9.6 awk单行程序实战案例
awk擅长写短小单行处理,下面直接复制可用。
|
|
9.7 语句
9.7.1 顺序执行
语句用分号分隔;大括号{ ... }复合语句块。
9.7.2 if条件
9.7.3 循环4种
- while
- do‑while
- for(初始化;条件;自增)
注意:浮点数不要做for循环,浮点精度误差会出bug。
- for-in遍历关联数组
循环控制:
break:跳出最内层循环;awk不支持break N,不能跳多层continue:跳到下一轮循环
9.7.4 数组成员测试 key in array
重要:判断key是否存在数组,不要直接拿
arr[key] != ""判断,访问不存在key会自动创建该数组元素!
✅正确
❌错误,会新增空元素
9.7.5 流程控制关键字
next:跳过当前记录剩下所有模式匹配,直接读下一行记录nextfile(gawk扩展):直接关闭当前文件,跳到下一个输入文件。exit [n]:立刻终止awk程序,返回退出码n给shell。
9.7.6 getline 手动读取输入
awk默认自动循环读记录;getline可以手动读取记录。
| 语法 | 作用 | 返回值 |
|---|---|---|
getline |
读取下一行到$0 |
1成功,0文件结束,‑1错误 |
getline var |
读取一行存入变量var,不修改$0 | 同上 |
getline < "file.txt" |
从文件读记录 | 同上 |
cmd | getline |
执行shell命令,读取命令输出一行 | 同上 |
重点:不是所有字符串都会执行!只有字符串出现在 | getline 左边的时候,awk 才会把这个字符串交给 shell 当做命令运行。 是 | getline 这个语法触发把字符串当做命令跑。
打开文件/管道记得
close("文件名"),避免打开文件数耗尽。
示例读取命令输出:
9.7.7 输出重定向
print输出可以重定向到文件、管道。
>覆盖写文件>>追加写文件| cmd输出管道交给shell命令
awk的文件打开之后不会自动关闭,用完务必
close()!
9.7.8 system()执行外部shell命令
system()是 awk 的内置函数,专门用来调用执行外部 shell 命令。system("shell命令字符串"),返回命令退出状态码。- system会新开shell进程;输出直接打印,不会捕获到awk变量。
system("ls -l")- awk 启动一个新
/bin/shshell 子进程,执行字符串里面的 shell 命令ls -l。 - 命令的标准输出、标准错误,直接打印到屏幕上,直接输出,不会存进 awk 变量!
- awk 启动一个新
- 返回值
ret:拿到的是 shell 命令的退出状态码(0 代表命令执行成功;非 0 代表出错)。
⚠巨大区别:
system()不能捕获命令输出到 awk 变量!输出直接往外跑。 如果你想要拿到命令输出存到 awk 变量,就不能用 system,要用我们刚才学的"cmd" | getline var管道写法。
对比两套方案
方案 1:system () —— 执行命令,拿退出码;拿不到输出
运行输出:
date 的时间直接输出,没有保存到 awk 变量。ret 只存 0(成功)。
方案 2:"date" | getline now —— 捕获命令输出到变量
输出不会直接打印,放到 awk 变量now里面,我们自己控制什么时候 print。
简单总结对照表
| 方式 | 作用 | 能不能拿到命令输出到awk变量 | 得到什么返回值 |
|---|---|---|---|
system("cmd") |
执行shell命令 | ❌不能,输出直接打印屏幕 | 得到命令退出状态码 |
"cmd" | getline var |
执行shell命令 | ✅可以,输出读到var变量 | getline返回1/0/-1(读取成功/EOF/错误) |
坑提醒
system()里面写的是 shell 命令字符串,shell 的特殊符号* > |都生效。
|
|
- system 每调用一次就新建一个全新 shell 进程;多次 system 之间变量环境互相不传递。
两个 system 是两个独立子 shell,不能靠 system 在两次调用之间传递变量。
9.8 用户自定义函数
awk支持自定义函数;标量传值,数组传引用。
函数参数列表末尾写额外变量作为局部临时变量(awk没有专门local关键字)。
awk 自定义函数局部变量规则
awk 没有
local关键字,不能显式声明局部变量。
规则:
function 函数名(参数1,参数2, 局部变量1,局部变量2)
逗号分隔,在参数列表里面,多写几个变量名,调用函数的时候不给它传实参;这些变量就变成函数内部的局部变量。
习惯写法:前面是真正接收调用传入的参数,后面打两个空格隔开,之后全部是局部临时变量。
语法:
注意逗号:b, local1,b后面逗号,然后习惯敲两个空格,视觉上区分【真实入参】和【局部变量】。
local1 local2 并不是调用的时候要传的参数,只是写在这里,变成函数私有的局部变量。
例子演示
写一个函数,求两个数的和,中间需要一个临时变量tmp做中转。
正确写法:参数列表末尾增加临时局部变量
运行输出:8
调用:
add(3,5),只给x、y赋值。tmp没有传入任何值,但是它是函数内部私有局部变量,函数外面访问不到 tmp。
示例:查找数组value对应的key
array、value:调用传入的真实参数key:放在参数列表末尾,局部临时循环变量;调用的时候不会传 key 进来
调用示例:
调用只传两个实参,key是函数内部循环用的局部变量。
支持递归,但注意递归深度,过大递归会栈溢出。
9.9 字符串内置函数
| 函数 | 说明 | 示例 | 示例输出 |
|---|---|---|---|
length(s) |
返回字符串长度 | length("abcde") |
5 |
substr(s,start [,len]) |
截取子串,下标从1开始 省略len:截取到字符串末尾 | substr("abcde",2,3) substr("abcde",3) |
"bcd" "cde" |
tolower(s) |
全部转为小写 | tolower("AbC123") |
"abc123" |
toupper(s) |
全部转为大写 | toupper("AbC123") |
"ABC123" |
index(s,t) |
在s里面找子串t,返回起始位置;找不到返回0 |
index("abcde","cd") index("abcde","xy") |
3 0 |
match(s,regex) |
正则匹配;匹配成功设置内置变量RSTART(起始位置)、RLENGTH(匹配到的长度);匹配失败返回0 |
BEGIN{s="hello123world"; match(s,/[0-9]+/); print RSTART,RLENGTH} |
6 3 |
sub(re,repl,target) |
替换第一个匹配到的内容;省略第三个参数默认操作$0 |
BEGIN{s="a1 b1 c1"; sub(/1/,"9",s); print s} |
"a9 b1 c1" |
gsub(re,repl,target) |
全局全部替换所有匹配;省略第三个参数默认操作$0 |
BEGIN{s="a1 b1 c1"; gsub(/1/,"9",s); print s} |
"a9 b9 c9" |
split(str,arr,regex) |
按正则分割字符串,结果存入数组arr;返回分割出来元素的个数 |
BEGIN{n=split("aa:bb:cc",arr,":"); print n,arr[1],arr[2],arr[3]} |
3 aa bb cc |
sprintf(fmt,...) |
格式化,返回格式化后的字符串,不会打印,存给变量用 | BEGIN{s=sprintf("num=%04d",12); print s} |
"num=0012" |
printf(fmt,...) |
格式化,直接输出打印,不返回值 | BEGIN{printf("num=%04d\n",12)} |
num=0012 |
sub/gsub替换字符串中&代表匹配到原文;要字面&写\\&。
split示例:
9.10 数值函数
| 函数 | 作用 |
|---|---|
int(x) |
取整数部分,截断小数 |
sqrt(x) |
平方根 |
sin/cos/atan2(y,x) |
三角函数,参数弧度 |
exp(x) log(x) |
指数,自然对数 |
rand() |
返回0 ≤ r <1的随机浮点数 |
srand([seed]) |
设置随机种子;不带参数使用系统时间做种子 |
⚠注意:不调用
srand(),每次运行awk得到的随机序列完全一样!
9.11 小结
- awk模型:数据驱动,自动遍历记录,
pattern {action}模式操作对,学习重点。 - 内置变量
FS/OFS/RS/ORS/NR/FNR/NF是awk灵魂,控制记录字段解析。 - 关联数组,索引可以字符串;区分数组key存在判断,不要直接取值判断空。
- 字符串拼接不需要运算符;
substr/index/match/split/sprintf高频字符串函数。 - 可以
getline手动读;system()执行外部命令;输出支持重定向文件管道,记得close。 - 支持自定义函数;标量传值,数组传引用;没有local,参数列表预留名字充当局部变量。
- 大量简短单行awk可以替代sed/grep/cut等多个工具。
坑提醒:
- awk全部浮点数,循环不要用小数做条件;
- 修改$1等字段才会用OFS重建$0;单纯print $0不会生效OFS;
- 打开文件管道必须close,不然耗尽文件句柄;
- 数组
key in arr才是正确判断key是否存在;直接读取会新增元素。
文章作者 会写代码的小郎中
上次更新 2011-07-22
许可协议 CC BY-NC-ND 4.0