文章目录
getline用法
getline
是 awk
中用于读取下一行或从文件、管道获取输入的命令。
它的使用会改变 awk
的一些内置变量,如 NF
(字段数)、NR
(当前记录数)、$0
(当前记录内容)、FNR
(当前文件的记录数)等。
以下是 getline
的几种常用形式:
-
基本用法(无重定向和管道符):
-
当
getline
没有重定向符号(<
、>
)或管道符号(|
)时,awk
首先读取的是当前行(第一行),而getline
获取的是光标跳转至下一行(也就是第二行)的内容。例如:awk '{getline; print $0}' test1.txt
上述命令相当于打印偶数行,因为
getline
使得光标跳转到下一行。 -
如果把
getline
放在print
后:awk '{print $0; getline}' test1.txt
相当于打印奇数行,
awk
首先打印当前行的内容,然后getline
跳到下一行,awk
不再处理这行。
-
-
使用重定向或管道符号:
-
当
getline
两侧有管道符号或重定向符号时,getline
会对指定文件或命令的输出进行操作。因为文件刚被打开时,还没有被awk
读入任何内容,所以getline
读取的是文件的第一行,而不是跳转到下一行。原因:getline运行之后awk会改变NF,NR,$0,FNR等内部变量,所以此时读取$0的行号不再为1,而是2。
例如:
使用重定向将test1.txt
的内容输出到test2.txt
。awk '{getline < "test1.txt"; print $0 > "test2.txt";}' test1.txt
-
-
变量读取示例:
-
将
ls
命令的输出传递给getline
函数:line
是一个变量,将ls
的内容读入到变量line
,然后打印当前行和line
的内容(如果无内容,不做任何操作)。ls | awk '{getline line; print $0, line;}'
-
文本内容匹配过滤打印
-
匹配行以
root
开头并打印:awk '/^root/{print}' /etc/passwd
-
匹配行以
bash
结尾并打印:awk '/bash$/{print}' /etc/passwd
BEGIN 和 END 模式
awk
支持在处理数据之前和之后执行一些命令操作。BEGIN
块在数据处理开始前执行,END
块在数据处理结束后执行。
-
基本格式:
awk 'BEGIN{...};{...};END{...}' 文件
BEGIN
块:在处理指定的文本之前,先执行BEGIN
块中的命令操作。- 主处理块
{...}
:用于处理文件内容的主命令操作。 END
块:在处理完所有文件内容后,执行END
块中的命令操作。
-
示例:
awk 'BEGIN{x=1}; {x++}; END{print x}' test1.txt
计算文件
test1.txt
的行数(包括初始值x=1
),最后输出结果10
。
对字段进行处理打印
-
指定分隔符并打印特定列:
head -n5 /etc/passwd | awk -F: '{print $1}' # 使用冒号 `:` 分隔并打印第一列 head -n5 /etc/passwd | awk -F: '{print $2}' # 使用冒号 `:` 分隔并打印第二列
-v 选项的用法:变量赋值
-v
选项用于给 awk
脚本中的变量赋值。
-
通过
-v
传递变量:FS
(字段分隔符)被设置为:
,OFS
(输出字段分隔符)被设置为+
。然后打印第一列和第三列。fs=":"; awk -v FS=$fs -v OFS="+" '{print $1, $3}' /etc/passwd # 输出: root+0 bin+1 daemon+2 adm+3 lp+4
-
从 Shell 传递变量到
awk
:将 Shell 变量
a
和b
传递给awk
,并计算它们的和。a=3 b=4 num=$(awk -v a="$a" -v b="$b" 'BEGIN{print a + b}')
-
普通赋值变量:
使用冒号
:
作为输入字段分隔符,==
作为输出字段分隔符,打印第一列和第三列。awk -v FS=':' -v OFS='==' '{print $1, $3}' /etc/passwd # 输出: root==0 bin==1 daemon==2 adm==3 lp==4 sync==5
-
按行输出
$PATH
环境变量中的各个路径:$PATH
环境变量包含一系列用冒号分隔的目录路径,系统会在这些目录中查找可执行命令。echo $PATH | awk -v RS=':' '{print $1}' # 默认就是换行输出,不需要更改 # 输出示例: /usr/local/sbin /usr/local/bin /usr/sbin /usr/bin /root/bin
解释:
echo $PATH
:打印出当前环境变量$PATH
的内容。$PATH
包含多个目录路径,这些路径用冒号(:
)分隔。awk -v RS=':' '{print $1}'
:awk
:一种强大的文本处理工具。v RS=':'
:设置记录分隔符(Record Separator,RS
)为冒号(:
)。默认情况下,awk
以换行符作为分隔符;此处将其更改为冒号,以便逐个读取$PATH
中的每个目录。{print $1}
:表示输出每条记录的第一个字段(这里的每条记录即为一个目录路径)。
BEGIN 模式指定
-
改变分隔符并打印:
在awk
执行前使用BEGIN
块改变分隔符为:
,然后在执行过程中以冒号分隔字段并打印第五列。head -n5 /etc/passwd | awk 'BEGIN{FS=":"}; {print $5}'
条件判断打印
- 条件判断打印特定行:
-
打印
uid
大于500
的行:awk -F: '$3>500{print $0}' /etc/passwd
-
取反打印大于
10
,为uid
小于10
的行:awk -F: '!($3>10){print $0}' /etc/passwd
-
使用
if
语句进行判断:
使用if
语句,()
内部是条件,{}
外部是执行语句块,相当于嵌套语法。awk -F: '{if ($3>500){print $0}}' /etc/passwd
-
awk 的三元表达式
awk 的三元表达式继承了 Java 的用法,格式与 Java 相似。
格式:
awk '(条件表达式) ? (A 表达式或者值) : (B 表达式或者值)'
示例:
awk -F: '{max=($3 >= $4) ? $3 : $4; {print max, $0}}' /etc/passwd | sed -n '1,6p'
这个命令的作用是比较 /etc/passwd
文件中以 :
为分隔符的第三个和第四个字段的大小,并输出结果。
max=($3 >= $4) ? $3 : $4
:这行代码使用了三元运算符来设置变量max
,如果字段 3 大于等于字段 4,则max
为字段 3 的值,否则为字段 4 的值。? :
运算符是if-else
语句的简写,因此此行等效于if ($3 >= $4) { max = $3 } else { max = $4 }
。print max, $0
:打印出max
和当前行的内容。
最终结果是输出比较后的最大值 max
,并打印文件的前 6 行。
awk 的精准筛选
awk 允许通过特定条件表达式筛选文本。
$n(> < ==)
:用于对比数值。$n~"字符串"
:表示第 n 个字段包含某个字符串。$n!~"字符串"
:表示第 n 个字段不包含某个字符串。$n=="字符串"
:表示第 n 个字段为某个字符串。$n!="字符串"
:表示第 n 个字段不为某个字符串。$NF
:表示最后一个字段。
实例:
-
输出第七个字段包含“bash”所在行的第一个字段和最后一个字段:
awk -F: '$7~"bash" {print $1, $NF}' /etc/passwd
-
输出第七个字段不包含“nologin”所在行的第一个字段和最后一个字段:
awk -F: '$7!~"nologin" {print $1, $NF}' /etc/passwd
-
指定第六个字段为
/home/dn
,第七个字段为/bin/bash
,输出满足这些条件所在行的第一个和最后一个字段:awk -F: '($6=="/home/dn") && ($7=="/bin/bash") {print $1, $NF}' /etc/passwd
awk 的分隔符用法
RS 指定行分隔符
awk 从文件中读取资料时,将根据 RS
的定义把资料切割成多条记录,awk 一次仅读入一条记录进行处理。内置变量 RS
的默认值是 "\n"
(换行符)。可以使用 BEGIN
模式在操作前改变行分隔符。
示例:
将 :
作为行分隔符,分割后逐行打印内容和行号。
echo $PATH | awk 'BEGIN{RS=":"};{print NR, $0}'
指定输出的分隔符
OFS
(输出字段分隔符)用于指定输出内容的列分隔符。注意:$n=$n
用于激活分隔符,否则不生效,n
必须存在。
示例:
-
使用
tr
改变分隔符输出:echo a b c d | tr " " ":"
-
使用
awk
改变输出分隔符:echo a b c d | awk '{OFS=":"; $1=$1; print $0}' echo a b c d | awk 'BEGIN{OFS=":"}; {$2=$2; print $0}'
awk 结合数组运用
awk 中定义数组打印
示例:
输出数组 a
中索引为 1 的值,即 20
。
awk 'BEGIN{a[0]=10; a[1]=20; a[2]=30; print a[1]}'
awk 中的数组遍历
示例:
遍历数组 a
,打印索引和值。(在awk中打印变量不需要加 $
)
awk 'BEGIN{a[0]=10; a[1]=20; a[2]=30; for(i in a) print i, a[i]}'
处理文件去重统计
通过 awk
可以方便地统计文件中每个单词的出现次数。(在awk中打印变量不需要加 $
)
示例:
cat test3.txt
aaa
aaa
bbb
ccc
aaa
bbb
aaa
awk '{a[$1]++}; END{for(i in a){print i, a[i]}}' test3.txt
解释说明:
a[$1]++
:将当前行的第一个字段作为索引,数组a
对应的值加一。END{...}
:在文本读取完毕后,执行END
块中的命令。- 最终输出每个唯一字段及其在文件中出现的次数。
调试示例:
awk -v debug=1 '{print "Processing line", NR, "with value", $1; a[$1]++}; END{for(i in a){print "Result for value", i, "is", a[i]}}' test2.txt
用于检测 awk
的运行机制。
这种逐行读取和处理的方式,能够高效地对数据进行去重统计。
原理:
a[$1]初始为0,a[$1]++后即为1,
而这里awk中的a[$1]++最终的值是由test2.txt文本内容有多少行决定的,awk本身就是编程语言,不要按照shell来
文本逐行读取完毕后再执行END中的命令
awk会按行读取文件test2.txt的内容,然后逐个单词存储到数组a中。
每个单词都作为数组a的索引,所以每个单词都对应一个值,初始值为0。
读取文件的第一行,单词为 aaa,此时a[aaa]为0,执行 a[$1]++ 后,a[aaa]变成1。
读取文件的第二行,单词为 aaa,此时a[aaa]为1,执行 a[$1]++ 后,a[aaa]变成2。
读取文件的第三行,单词为 bbb,此时a[bbb]为0,执行 a[$1]++ 后,a[bbb]变成1。
读取文件的第四行,单词为 ccc,此时a[ccc]为0,执行 a[$1]++ 后,a[ccc]变成1。
读取文件的第五行,单词为 aaa,此时a[aaa]为2,执行 a[$1]++ 后,a[aaa]变成3。
读取文件的第六行,单词为 bbb,此时a[bbb]为1,执行 a[$1]++ 后,a[bbb]变成2。
读取文件的第七行,单词为 aaa,此时a[aaa]为3,执行 a[$1]++ 后,a[aaa]变成4。
最终,数组a中的元素值变成:a[aaa]=4,a[bbb]=2,a[ccc]=1。