可以查看我用CmdMarkDown写的在线版,会比下面看起来舒服:在线分享版
Awk
Awk
、sed
与grep
,俗称Linux
下的三剑客,它们之前有很多相似点,但是同样也各有各的特色,相似的地方是它们都可以匹配文本,其中sed
和awk
还可以用于文本编辑,而grep
则不具备这个功用。sed
是一种非交互式且面向字符流的编辑器(a “non-interactive” stream-oriented editor)
,而awk
则是一门模式匹配的编程语言,因为它的主要功能是用于匹配文本并处理,同时它有一些编程语言才有的语法,例如函数、分支循环语句、变量等等,当然比起我们常见的编程语言,Awk
相对比较简单。
使用Awk,我们可以做以下事情:
-
将文本文件视为由字段和记录组成的文本数据库;
-
在操作文本数据库的过程中能够使用变量;
-
能够使用数学运算和字符串操作;
-
能够使用常见的编程结构,例如条件分支与循环;
-
能够格式化输出;
-
能够自定义函数;
-
能够在awk脚本中执行UNIX命令;
-
能够处理UNIX命令的输出结果;
awk
的命令行语法也有两种形式:
awk [-F ERE] [-v assignment] ... program [argument ...]
awk [-F ERE] -f progfile ... [-v assignment] ...[argument ...]
将awk
的脚本视为一段代码。而awk
的脚本同样可以写到一个文件中,并通过-f
参数指定。program
一般由多个pattern
和action
序列组成,当读入的记录匹配pattern
时,才会执行相应的action
命令。这里有一点要注意,在第一种形式中,除去命令行选项外,program
参数一定要位于第一个位置。
Awk
的输入被解析成多个记录(Record)
,默认情况下,记录的分隔符是\n
,因此可以认为一行就是一个记录,记录的分隔符可以通过内置变量RS
更改。当记录匹配某个pattern
时,才会执行后续的action
命令。
内建标量变量:
FILENAME
- 当前输入文件的名称;FNR
- 当前文件的记录数;FS
- 字段分隔字符;NF
- 当前记录的字段数;NR
- 在工作中的记录数;OFS
- 输出字段分隔字符;ORS
- 输出记录分隔字符;RS
- 输入记录分隔字符。
而每个记录由进一步地被分隔成多个字段(Field)
,默认情况下字段的分隔符是空白符,例如空格、制表符等等,也可以通过-F ERE
选项或者内置变量FS
更改。在awk
中,可以通过$1
,$2
…来访问对应位置的字段,同时$0
存放整个记录,这一点有点类似shell
下的命令行位置参数。关于这些内容,我们会在下面详细介绍,这里你只要知道有这些东西就好。
标准的awk
命令行参数主要由以下三个:
-
-F ERE
:定义字段分隔符,该选项的值可以是扩展的正则表达式(ERE); -
-f progfile
:指定awk脚本,可以同时指定多个脚本,它们会按照在命令行中出现的顺序连接在一起; -
-v assignment
:定义awk
变量,形式同awk
中的变量赋值,即name=value
,赋值发生在awk
处理文本之前;
通过-F
参数设置冒号:
为分隔符,并打印各个字段:
$ echo "1:2:3" | awk -F: '{print $1 " and " $2 " and " $3}'
1 and 2 and 3
在awk
的脚本中访问通过-v
选项设置的变量:
$ echo | awk -v a=1 'BEGIN {print a}'
1
从上面可以看到,通过-v
选项设置的变量在BEGIN
的位置就可以访问了。BEGIN
是一个特殊的pattern
,它在awk
处理输入之前就会执行,这个时候内建的标量变量都没有值,因为文本还没有开始读入,可以认为是一个初始化语句,与此对应的还有END
。
arguemnt
有两种形式,它们分别是输入文件(file)
和变量赋值(assignment)
。
awk
可以同时指定多个输入文件,如果输入文件的文件名为-
,表示从标准输入读取内容。
变量赋值类似-v
选项,它的形式为name=value
。awk
中的变量名同一般的编程语言无太多区别,但是不能同awk
的保留关键字重名,可以查看awk
的man
手册查询哪些是保留关键字。而变量值只有两种形式:字符串和数值。变量赋值必须位于脚本参数的后面,与文件名参数无先后顺序的要求,但是位于不同位置的赋值它的执行时机是不同的。
用实际的例子来解释这个区别,假设有两个文件:a
和b
,它们的内容分别如下所示:
$ cat a
file a
$ cat b
file b
为了说明赋值操作发生的时机,我们在BEGIN
,正常处理,END
三个地方都打印变量的值。
- 第一种情况: 变量赋值位于所有文件名参数之前
$ awk 'BEGIN {print "BEGIN: " var} {print "PROCESS: " var} \
END {print "END: " var }' var=1 a
BEGIN:
PROCESS: 1
END: 1
结果:赋值操作发生在正常处理之前,BEGIN
动作之后。
- 第二种情况:变量赋值位于所有文件名之后:
$ awk 'BEGIN {print "BEGIN: " var} {print "PROCESS: " var} \
END {print "END: " var }' a var=1
BEGIN:
PROCESS:
END: 1
结果:赋值操作发生在正常处理之后,END动作之前。
第三种情况:变量赋值位于文件名之间:
$ awk 'BEGIN {print "BEGIN: " var} {print "PROCESS: " var} \
END {print "END: " var }' a var=1 b
BEGIN:
PROCESS:
PROCESS: 1
END: 1
结果:赋值操作发生在处理前面的文件之后,并且位于处理后面的文件之前;
总结如下:
-
如果变量赋值在第一个文件参数之前,在
BEGIN
动作之后执行,影响到正常处理和END
动作; -
如果变量赋值在最后一个文件参数之后,在
END
动作之前执行,仅影响END
动作; -
如果文件参数不存在,情况同1所述;
-
如果变量赋值位于多个文件参数之间,在变量赋值前面的文件被处理后执行,影响到后续文件的处理和
END
动作;
所以变量赋值一定要考虑清楚用途,否则比较容易出错,不过一般情况下也不会用到变量赋值。
自然地大家会将变量赋值与-v assignment
选项进行比较,赋值的形式是一致的,但是-v选项的执行时机比变量赋值要早:
$ echo 1 | awk -v var=a 'BEGIN {print "BEGIN: " var}'
BEGIN: a
可见,-v
选项的赋值操作在BEGIN
动作之前就执行了。
变量赋值一定要小心不要与保留关键字重名,否则会报错:
$ echo 1 | awk -v BEGIN=1 'BEGIN {print "BEGIN: " BEGIN}'
awk: fatal: cannot use gawk builtin `BEGIN' as variable name
记录(Record)与字段(Field)
对于数据库来说,一个数据库表是由多条记录组成的,每一行表示一条记录(Record)
。每条记录由多列组成,每一列表示一个字段(Field)
。Awk
将一个文本文件视为一个文本数据库,因此它也有记录和字段的概念。默认情况下,记录的分隔符是回车,字段的分隔符是空白符,所以文本文件的每一行表示一个记录,而每一行中的内容被空白分隔成多个字段。利用字段和记录,awk
就可以非常灵活地处理文件的内容。
可以通过-F
选项来修改默认的字段分隔符,例如/etc/passwd
的每一行都是由冒号分隔成多个字段的,所以这里就需要将分隔符设置成冒号:
$ awk -F: '{print $1}' /etc/passwd | head -3
root
bin
daemon
这里通过$1
引用第一人字段,类似地$2
表示第二个字段,$3
表示第三个字段…. $0
则表示整个记录。内置变量NF
记录着字段的个数,所以$NF
表示最后一个字段:
$ awk -F: '{print $NF}' /etc/passwd | head -3 #最后一个字段
/bin/bash
/bin/false
/bin/false
当然,$(NF-1)
表示倒数第二个。
内置变量FS
也可以用于更改字段分隔符,它记录着当前的字段分隔符:
$ awk -F: '{print FS}' /etc/passwd | head -1
:
$ awk -v FS=: '{print $1}' /etc/passwd | head -1
root
记录的分隔符可以通过内置变量RS
更改:
$ awk -v RS=: '{print $0}' /etc/passwd | head -1
root
如果将RS
设置成空,行为有就一点怪异了,它会将连续不为空行的所有行(一个段落)当作一个记录,而且强制回车为字段分隔符:
$ cat awk_man.txt
The awk utility shall execute programs written in the awk programming language,
which is specialized for textual data manipulation. An awk program is a sequence
of patterns and corresponding actions. When input is read that matches a
pattern, the action associated with that pattern is carried out.
Input shall be interpreted as a sequence of records. By default, a record is a line,
less its terminating <newline>, but this can be changed by using the RS built-in
variable. Each record of input shall be matched in turn against each pattern in the
program. For each pattern matched, the associated action shall be executed.
$ awk 'BEGIN {RS="";FS=":"} {print "First line: " $1}' awk_man.txt
First line: The awk utility shall execute programs written in the awk programming language,
First line: Input shall be interpreted as a sequence of records. By default, a record is a line,
这里,我们将变量赋值放到BEGIN
动作中执行,因为BEGIN
动作是在文件处理之前执行的,专门用于放初始化的语句。FS
的赋值在这里是无效的,awk
依然使用回车符来分隔字段。
脚本(Script)组成
命令行中的program
部分,可以称为awk
代码,也可以称为awk
脚本。一段awk
脚本是由多个pattern { action }
序列组成的。action
是一个或者多个语句,它在输入行匹配pattern
的时候被执行。如果pattern
为空,表明这个action
会在每一行处理时都会被执行。下面的例子简单地打印文件的每一行,这里不带任何参数的print
语句打印的是整个记录,类似print $0
:
$ echo -e 'line1\nline2' | awk '{print}'
line1
line2
除了pattern { action }
,还可以在脚本中定义自定义的函数,函数定义格式如下所示:
function name(parameter list) { statements }
函数的参数列表用逗号分隔,参数默认是局部变量,无法在函数之外访问,而在函数中定义的变量为全局变量,可以在函数之外访问,如:
$ echo line1 | awk '
function t(a) {
b=a;
print a;
}
{
print b;
t("kodango.me");
print b;
}'
kodango.me
kodango.me
Awk
脚本中的语句使用空行或者分号分隔,使用分号可以放在同一行,不过有时候会影响可读性,尤其是分支或循环结构中,很容易出错。
如果Awk
中的一个语句太长,要分成多行,可以在行为使用反斜杠\
:
$ cat test.awk
function t(a)
{
b=a
print "This is a very long line, so use backslash to escape the newline \
then we will print the variable a: a=" a
}
{ print b; t("kodango.me"); print b;}
$ echo 1 | awk -f test.awk
This is a very long line, so use backslash to escape the newline then we will print the variable a: a=kodango.me
kodango.me
这里我们将脚本写到文件中,并通过-f
参数来指定。但是,在一些特殊符号之后,是可以直接换行的,例如, { && ||
。
模式(Pattern)
模式是awk
中比较重要的一部分,它有以下几种情况:
/regular expression/
: 扩展的正则表达式(Extended Regular Expression)
relational expression
: 关系表达式,例如大于、小于、等于,关系表达式结果为true
表示匹配;-
BEGIN
: 特殊的模式,在第一个记录处理之前被执行,常用于初始化语句的执行; -
END
: 特殊的模式,在最后一个记录处理之前被执行,常用于输出汇总信息; -
pattern, pattern
:模式对,匹配两者之间的所有记录;
例如查找匹配数字3的行:
$ seq 1 20 | awk '/3/ {print}'
3
13
相反地,可以在在正则表达式之前加上!
表示不匹配:
$ seq 1 5 | awk '!/3/ {print}'
除了BEGIN
和END
这两个特殊的模式外,其余的模式都可以使用&&
或者||
运算符组合,前者表示逻辑与,后者表示逻辑或:
$ seq 1 50 | awk '/3/ && /1/ {print}'
13
31
前面的正则都是整行匹配,有时候仅仅需要匹配某个字符,这样我们可以用表达式$n ~ /ere/
:
$ awk '$1 ~ /ko/ {print}' /etc/passwd #每一行的第一个字段匹配ko
kodango:x:1000:1000::/home/kodango:/bin/bash
有时候我们只想显示特定和行,例如显示第一行:
$ seq 1 5 | awk 'NR==1 {print}'
数组
数组是一种特殊的变量,在awk
中,比较特殊地是,数组的下标可以为数字或者字符串。数组的赋值很简单,下面将value
赋值给数组下标为index
的元素:
array[index]=value
可以用for..in..
语法遍历数组元素,其中item
是数组元素对应的下标(是随机遍历的):
for (item in array)
当然也可以在if
分支判断中使用in
操作符:
if (item in array)
一个完整的例子如下所示:
$ echo "1 2 3" | awk '{
for (i=0;i<NF;i++)
a[i]=i;
}
END {
print 3 in a
for (i in a)
printf "%s: %s\n", i, a[i];
}'
0
0: 0
1: 1
2: 2
内置变量
Awk
在内部维护了许多内置变量,或者称为系统变量,例如之前提到的FS
、RS
等等。常见的内置变量如下表所示
- 变量名 描述
ARGC
命令行参数的个数,即ARGV
数组的长度ARGV
存放命令行参数CONVFMT
定义awk
内部数值转换成字符串的格式,默认值为%.6g
OFMT
定义输出时数值转换成字符串的格式,默认值为%.6g
ENVIRON
存放系统环境变量的关联数组NR
记录的总个数FNR
当前文件中的记录的总个数FS
字段分隔符,默认为空白NF
每个记录中字段的个数RS
记录的分隔符,默认为回车OFS
输出时字段的分隔符,默认为空白ORS
输出时记录的分隔符,默认为回车RLENGTH
被match
函数匹配的子串长度-
RSTART
被match
函数匹配的子串位于目标字符串的起始下标- ARGV与ARGC
ARGV
与ARGC
的意思比较好理解,就像C
语言main(int argc, char **argv)
。ARGV
数组的下标从0
开始到ARGC-1
,它存放的是命令行参数,并且排除命令行选项(例如-v/-f
)以及program
部分。因此事实上ARGV
只是存储argument
的部分,即文件名(file)
以及命令行变量赋值两部分的内容。
通过下面的例子可以大概了解ARGC
与ARGV
的用法:
$ awk 'BEGIN {
> for (i = 0; i < ARGC; i++)
> print ARGV[i]
> }' inventory-shipped BBS-list
awk
inventory-shipped
BBS-list
ARGV
的用法不仅限于此,它是可以修改的,可以更改数组元素的值,可以增加数组元素或者删除数组元素。
a. 更改ARGV元素的值
假设我们有a, b两个文件,它们各有一行内容:file a和file b。现在利用ARGV,我们可以做到偷梁换柱:
$ awk 'BEGIN{ARGV[1]="b"} {print}' a
file b
这里要注意ARGV[1]="b"
的引号不能缺少,否则ARGV[1]=b
会将变量b
的值赋值给ARGV[1]
。
当awk
处理完一个文件之后,它会从ARGV
的下一个元素获取参数,如果是一个文件则继续处理,如果是一个变量赋值则执行赋值操作:
$ awk 'BEGIN{ARGV[1]="var=1"} {print var}' a b
1
为什么这里只打印一次变量值呢?
可以回头再看看上一篇中介绍变量赋值的内容。
而当下一个元素为空时,则跳过不处理,这样可以避开处理某个文件:
$ awk 'BEGIN{ARGV[1]=""} {print}' a b
file b
上面的例子中a
这个文件就被跳过了。
而当下一个元素的值为-
时,表明从标准输入读取内容:
$ awk 'BEGIN{ARGV[1]="-"} {print}' a b
a
a # --> 这里按下CTRL+D停止输入
file b
b. 删除ARGV元素
删除ARGV元素和将元素的值赋值为空的效果是一样的,它们都会跳转对某个参数的处理:
$ awk 'BEGIN{delete ARGV[1]} {print}' a b
file b
删除数组元素可以用delete
语句。
- CONVFMT与OFMT
Awk
中允许数值到字符串相互转换,其中内置变量CONVFMT
定义了awk
内部数值到字符串转换的格式,它的默认值为%.6g
:
$ awk 'BEGIN {
printf "CONVFMT=%s, num=%f, str=%s\n", CONVFMT, 12.11, 12.11
}'
CONVFMT=%.6g, num=12.110000, str=12.11
通过更改CONVFMT
,我们可以定义自己的转换格式:
$ awk 'BEGIN {
CONVFMT="%d";
printf "CONVFMT=%s, num=%f, str=%s\n", CONVFMT, 12.11, 12.11
}'
CONVFMT=%d, num=12.110000, str=12
与此对应地还有一个内置变量OFMT
,它与CONVFMT
的作用是类似的,只不过是影响输出的时候数字转换成字符串的格式:
$ awk 'BEGIN { OFMT="%d";print 12.11 }'
12
- ENVIRON
ENVIRON
是一个存放系统环境变量的关联数组,它的下标是环境变量名称,值是相应环境变量的值。例如:
$ awk 'BEGIN { print ENVIRON["USER"] }'
kodango
利用环境变量也可以将值传递给awk
:
$ U=hello awk 'BEGIN { print ENVIRON["U"] }'
hello
可以利用for..in
循环遍历ENVIRON
数组:
$ awk 'BEGIN {
for (env in ENVIRON)
printf "%s=%s\n", env, ENVIRON[env];
}'
- RLENGTH与RSTART
RLENGTH
与RSTART
都是与match
函数相关的,前者表示匹配的子串长度,后者表示匹配的子串位于目标字符串的起始下标。例如:
$ awk 'BEGIN {match("hello,world", /llo/); print RSTART,RLENGTH}'
3 3
语句(Statement)
到目前为止,用得比较多的语句就是print
,其它的还有printf
、delete
、break
、continue
、exit
、next
等等。这些语句与函数不同的是,它们不会使用带括号的参数,并且没有返回值。不过也有意外,比如printf
就可以像函数一样的调用:
$ echo 1 | awk '{printf("%s\n", "abc")}'
abc
break
和continue
语句,大家应该比较了解,分别用于跳出循环和跳到下一个循环。
delete
用于删除数组中的某个元素,这个我们在上面介绍ARGV
的时候也使用过。
exit
的用法顾名思义,就是退出awk
的处理,然后会执行END
部分的内容:
$ echo $'line1\nline2' | awk '{print;exit} END {print "exit.."}'
line1
exit..
next
语句类似sed
的n
命令,它会读取下一条记录,并重新回到脚本的最开始处执行:
$ echo $'line1\nline2' | awk '{
> print "Before next.."
> print $0
> next
> print "After next.."
> }'
Before next..
line1
Before next..
line2
从上面可以看出next
后面的print
语句不会执行。
print
与printf
语句是使用最多的,它们将内容输出到标准输出。注意在print
语句中,输出的变量之间带不带逗号是有区别的:
$ echo "1 2" | awk '{print $1, $2}'
1 2
$ echo "1 2" | awk '{print $1 $2}'
12
print
输出时,字段之间的分隔符可以由OFS
重新定义:
$ echo "1 2" | awk '{OFS=";";print $1,$2}'
1;2
除此之外,print
的输出还可以重定向到某个文件中或者某个命令:
print items > output-file
print items >> output-file
print items | command
假设有这一样一个文件,第一列是语句名称,第二列是对应的说明:
$ cat column.txt
statement|description
delete|delete item from an array
exit|exit from the awk process
next|read next input record and process
现在我们要将两列的内容分别输出到statement.txt
和description.txt
两个文件中:
$ awk -F'|' '{
> print $1 > "statement.txt";
> print $2 > "description.txt"
> }' column.txt
$ cat statement.txt
statement
delete
exit
next
$ cat description.txt
description
delete item from an array
exit from the awk process
read next input record and process
下面是一个重定向到命令的例子,假设我们要对下面的文件进行排序:
$ cat num.list
1
3
2
9
5
可以通过将print
的内容重定向到sort -n
命令:
$ awk '{print | "sort -n"}' num.list
1
2
3
5
9
printf
命令的用法与print
类似,也可以重定向到文件或者输出,只不过printf
比print
多了格式化字符串的功能。printf
的语法也大多数语言包括bash
的printf
命令类似。
awk
的函数分成数学函数、字符串函数、I/O
处理函数以及用户自定义的函数
数学函数
awk
中支持以下数学函数:
● atan2(y,x):反正切函数;
● cos(x):余弦函数;
● sin(x):正弦函数;
● exp(x):以自然对数e为底指数函数;
● log(x):计算以e 为底的对数值;
● sqrt(x):绝对值函数;
● int(x):将数值转换成整数;
● rand():返回0到1的一个随机数值,不包含1;
● srand([expr]):设置随机种子,一般与rand函数配合使用,如果参数为空,默认使用当前时间为种子;
例如,我们使用rand()函数生成一个随机数值:
$ awk 'BEGIN {print rand(),rand();}'
0.237788 0.291066
$ awk 'BEGIN {print rand(),rand();}'
0.237788 0.291066
但是你会发现,每次awk
执行都会生成同样的随机数,但是在一次执行过程中产生的随机数又是不同的。因为每次awk
执行都使用了同样的种子,所以我们可以用srand()
函数来设置种子:
$ awk 'BEGIN {srand();print rand(),rand();}'
0.171625 0.00692412
$ awk 'BEGIN {srand();print rand(),rand();}'
0.43269 0.782984
这样每次生成的随机数就不一样了。
利用rand()
函数我们也可以生成1
到n
的整数:
$ awk '
> function randint(n) { return int(n*rand()); }
> BEGIN { srand(); print randint(10);
> }'
3
字符串函数
awk
中包含大多数常见的字符串操作函数。
- sub(ere, repl[, in])
描述:简单地说,就是将in
中匹配ere
的部分替换成repl
,返回值是替换的次数。如果in
参数省略,默认使用$0
。替换的动作会直接修改变量的值。
下面是一个简单的替换的例子:
$ echo "hello, world" | awk '{print sub(/ello/, "i"); print}'
1
hi, world
- gsub(ere, repl[, in])
描述:同sub()
函数功能类似,只不过是gsub()
是全局替换,即替换所有匹配的内容。
- index(s, t)
描述:返回字符串t
在s
中出现的位置,注意这里位置是从1
开始计算的,如果没有找到则返回0
。
例如:
$ awk 'BEGIN {print index("kodango", "o")}'
2
$ awk 'BEGIN {print index("kodango", "w")}'
0
- length[([s])]
描述:返回字符串的长度,如果参数s
没有指定,则默认使用$0
作为参数。
例如:
$ awk 'BEGIN {print length('kodango');}'
0
$ echo "first line" | awk '{print length();}'
10
- match(s, ere)
描述: 返回字符串s
匹配ere
的起始位置,如果不匹配则返回0
。该函数会定义RSTART
和RLENGTH
两个内置变量。RSTART
与返回值相同,RLENGTH
记录匹配子串的长度,如果不匹配则为-1
。
例如:
$ awk 'BEGIN {
print match("kodango", /dango/);
printf "Matched at: %d, Matched substr length: %d\n", RSTART, RLENGTH;
}'
3
Matched at: 3, Matched substr length: 5
- split(s, a[, fs])
描述:将字符串按照分隔符fs
,分隔成多个部分,并存到数组a
中。注意,存放的位置是从第1
个数组元素开始的。如果fs
为空,则默认使用FS
分隔。函数返回值分隔的个数。
例如:
$ awk 'BEGIN {
> split("1;2;3;4;5", arr, ";")
> for (i in arr)
> printf "arr[%d]=%d\n", i, arr[i];
> }'
arr[4]=4
arr[5]=5
arr[1]=1
arr[2]=2
arr[3]=3
这里有一个奇怪的地方是for..in..
输出的数组不是按顺序输出的,如果要按顺序输出可以用常规的for
循环:
$ awk 'BEGIN {
> split("1;2;3;4;5", arr, ";")
> for (i=0;^C
$ awk 'BEGIN {
> n=split("1;2;3;4;5", arr, ";")
> for (i=1; i<=n; i++)
> printf "arr[%d]=%d\n", i, arr[i];
> }'
arr[1]=1
arr[2]=2
arr[3]=3
arr[4]=4
arr[5]=5
- sprintf(fmt, expr, expr, ...)
描述:类似printf
,只不过不会将格式化后的内容输出到标准输出,而是当作返回值返回。
例如:
$ awk 'BEGIN {
> var=sprintf("%s=%s", "name", "value")
> print var
> }'
name=value
- substr(s, m[, n])
描述:返回从位置m
开始的,长度为n
的子串,其中位置从1
开始计算,如果未指定n
或者n
值大于剩余的字符个数,则子串一直到字符串末尾为止。
例如:
$ awk 'BEGIN { print substr("kodango", 2, 3); }'
oda
$ awk 'BEGIN { print substr("kodango", 2); }'
odango
- tolower(s)
描述:将字符串转换成小写字符。
$ awk 'BEGIN {print tolower("KODANGO");}'
kodango
- toupper(s)
描述:将字符串转换成大写字符。
例如
$ awk 'BEGIN {print tolower("kodango");}'
KODANGO
I/O处理函数
- getline
getline
的用法相对比较复杂,它有几种不同的形式。不过它的主要作用就是从输入中每次获取一行输入。
a. expression | getline [var]
这种形式将前面管道前命令输出的结果作为getline
的输入,每次读取一行。如果后面跟有var
,则将读取的内容保存到var
变量中,否则会重新设置$0
和NF
。
例如,我们将上面的statement.txt
文件的内容显示作为getline
的输入:
$ awk 'BEGIN { while("cat statement.txt" | getline var) print var}'
statement
delete
exit
next
上面的例子中命令要用双引号,cat statement.txt
,这一点同print/printf
是一样的。
如果不加var
,则直接写到$0
中,注意NF
值也会被更新:
$ awk 'BEGIN { while("cat statement.txt" | getline) print $0,NF}'
statement 1
delete 1
exit 1
next 1
b. getline [var]
第二种形式是直接使用getline
,它会从处理的文件中读取输入。同样地,如果var
没有,则会设置$0
,并且这时候会更新NF
, NR
和FNR
:
$ awk '{
> while (getline)
> print NF, NR, FNR, $0;
> }' statement.txt
1 2 2 delete
1 3 3 exit
1 4 4 next
c. getline [var] < expression
第三种形式从expression
中重定向输入,与第一种方法类似,这里就不加赘述了。
- close
close
函数可以用于关闭已经打开的文件或者管道,例如getline
函数的第一种形式用到管道,我们可以用close
函数把这个管道关闭,close
函数的参数与管道的命令一致:
$ awk 'BEGIN {
while("cat statement.txt" | getline) {
print $0;
close("cat statement.txt");
}}'
statement
statement
statement
statement
statement
但是每次读了一行后,关闭管道,然后重新打开又重新读取第一行就死循环了。所以要慎用,一般情况下也很少会用到close
函数。
- system
这个函数很简单,就是用于执行外部命令,例如:
$ awk 'BEGIN {system("uname -r");}'
3.6.2-1-ARCH