如何使用Awk打印在文件中字段和列
这是Linux Awk命令系列中第二部分,我们应该看一下Awk最重要特性之一为字段编辑。
Awk自动划分提供给它的输入行为字段,并且一个字段可以被定义为一个字符集合,由内部字段分隔符从其它字段分隔出这个字符集。
如果你熟悉Unix/Linux或者进行bash shell编程,接着你应该知道内部字段分隔符(IFS)是什么。在Awk中默认的IFS是tab或者space。
这就是字段分隔的想法如何在Awk中工作:当它遇到一输入行,根据定义的IFS,首个字符集是第一个字段,使用$1访问它,第二个字符集是第二个字段,使用$2访问它,第三个字符集是第三个字段,使用$3访问它,并且以此类推,直到最后一个字符集。
要更好地理解这种Awk字段编辑,我们看一下以下一些示例。
示例1:我创建了一个称作exer1.txt地文本文件。
[root@telecom exer]# vim exer1.txt
[root@telecom exer]# cat exer1.txt
This file is for practising the usage of Linux Awk command.
接着从这个命令行,使用以下命令从文件exer1.txt尝试打印第一、第二和第三字段:
[root@telecom exer]# awk '//{print $1 $2 $3}' exer1.txt
Thisfileis
从以上输出,我们看到根据定义的IFS(此处是space),打印了来自前三个字段的字符:
- 第一个字段是"This",使用$1访问。
- 第二个字段是"file",使用$2访问。
- 第三个字段是"is",使用$3访问。
如果你注意打印的输出,字段值未被分隔并且这是默认的打印行为。
要清楚地查看空格分隔的字段值,你需要按照以下添加(,)操作符:
[root@telecom exer]# awk '//{print $1,$2,$3;}' exer1.txt
This file is
要注意和总是记住的一件重要事情是在Awk中($)的使用不同于在shell脚本中它的使用。
在shell脚本中,($)用于访问变量的值,而在Awk中,($)只在访问一个字段内容时才使用而不是用于访问变量的值。
示例2:让我们看一下使用一个包含多行的文件(shoplist.txt)的示例
No Item_Name Unit_Price Quantity Price
1 Mouse #20,000 1 #20,000
2 Monitor #500,000 1 #500,000
3 RAM_Chips #150,000 2 #300,000
4 Ethernet_Cables #30,000 4 #120,000
你想要打印每行的Item_Name和Unit_Price,你将需要运行以下命令:
[root@telecom exer]# awk '//{print $2,$3}' shoplist.txt
Item_Name Unit_Price
Mouse #20,000
Monitor #500,000
RAM_Chips #150,000
Ethernet_Cables #30,000
Awk也有一个printf命令,它帮助你格式化你的输出,因而你可以更加清楚地看到输出。使用printf格式化Item_Name和Unit_Price:
[root@telecom exer]# awk '//{printf "%-15s %s\n",$2,$3}' shoplist.txt
Item_Name Unit_Price
Mouse #20,000
Monitor #500,000
RAM_Chips #150,000
Ethernet_Cables #30,000
如何用特定模式操作使用Awk过滤文本或者字符串
在Awk命令系列第三部分,我们应该看一下基于一个用户能够定义的特定模式过滤文本或字符串。
有时,当过滤文本时,你想要根据指定条件或者使用能够被匹配的特定模式表明来自一个输入文件或字符串行的特定行。用Awk做这件事非常容易,其是Awk的最好特性之一。
让我们看一下以下示例,就说你有一个想要购买的食物的购物清单food_prices.txt。它有食物项和它们价格的清单。
[root@telecom exer]# cat food_prices.txt
No Item_Name Quantity Price
1 Mangoes 10 $2.45
2 Apples 20 $1.50
3 Bananas 5 $0.90
4 Pineapples 10 $3.46
5 Oranges 10 $0.78
6 Tomatoes 5 $0.55
7 Onions 5 $0.45
并且接着,我们想要在食物项上用(*)符号表示其价格高于¥2,通过运行以下命令做这件事:
[root@telecom exer]# awk '/.*\$[2-9]\.[0-9][0-9]/ { print $1, $2, $3, $4, "*" ;} /.*\$[0-1]\.[0-9][0-9]/ {print ;}' food_prices.txt
1 Mangoes 10 $2.45 *
2 Apples 20 $1.50
3 Bananas 5 $0.90
4 Pineapples 10 $3.46 *
5 Oranges 10 $0.78
6 Tomatoes 5 $0.55
7 Onions 5 $0.45
从以上输出,我们可以看到在食物项mangoes和pineapples的行尾有一个(*)。如果你检查以下它们的价格,它们都高于$2。
在以上示例中,我们使用了两种模式:
- 模式1:/.*\$[2-9]\.[0-9][0-9]/ 获取价格高于$2的食物项所在行。
- 模式2:/.*\$[0-1]\.[0-9][0-9]/ 查找价格低于$2的食物项所在行。
这就是所发生的,在这个文件中有四个字段,如果模式1遇到了食物项价格高于$2的行,它打印所有四个字段并且在行尾处一个(*)符号作为一个标记。
模式2只是按照食物项价格低于$2的行在输入文件food_prices.txt中的样子打印处它们。
这种方式,你能够使用特定模式操作来过滤出价格高于$2的食物项,但输出有问题,有(*)符号的行未被格式化得像其余行,使得输出不够清晰。
1、使用printf命令,使用以下命令:
[root@telecom exer]# awk '/.*\$[2-9]\.[0-9][0-9]/ { printf "%-10s %-10s %-10s %-10s\n", $1, $2, $3, $4 "*";} /.*\$[0-1]\.[0-9][0-9]/ { printf "%-10s %-10s %-10s %-10s\n", $1, $2, $3, $4} ' food_prices.txt
1 Mangoes 10 $2.45*
2 Apples 20 $1.50
3 Bananas 5 $0.90
4 Pineapples 10 $3.46*
5 Oranges 10 $0.78
6 Tomatoes 5 $0.55
7 Onions 5 $0.45
2、使用$0字段。Awk使用变量0存储整个输入行。这对解决以上问题方便,并且它简单和快速:
[root@telecom exer]# awk '/.*\$[2-9]\.[0-9][0-9]\.*/ { print $0 "*";} /.*\$[0-1]\.[0-9][0-9]\.*/ { print ;} ' food_prices.txt
1 Mangoes 10 $2.45*
2 Apples 20 $1.50
3 Bananas 5 $0.90
4 Pineapples 10 $3.46*
5 Oranges 10 $0.78
6 Tomatoes 5 $0.55
7 Onions 5 $0.45
如何在Linux中对Awk使用比较操作符
当在文件行中处理数值或字符串值,使用比较操作符过滤文本或字符串对于Awk命令用户派上用场了。在这部分Awk系列中,我们看一下你如何使用比较操作符过滤文本或字符串。如果你是程序员,则你一定熟悉比较操作符,但对于不熟悉得人,让我们在下一部分解释。
在Awk中比较操作符是什么?
在Awk中比较操作符用于比较树枝或字符串,它们包括以下:
- >:大于
- <:小于
- >=:大于等于
- <=:小于等于
- ==:等于
- !=:不等于
- some_value ~ /pattern/:如果some_value匹配模式,true
- some_value !~ /pattern/:如果some_value不匹配模式,false
现在我们已经看到了在Awk中各种比较操作符,让我们使用示例更好理解它们。
在本例中,我们有一个名为food_list.txt的文件,它是一个用于食物项的购物清单并且我想要通过在数量小于等于20的食物项的行尾添加(**)来标记它们。
在Awk中用于比较操作符的一般语法是:
# expression {actions;}
要实现以上目标,我将必须运行以下命令:
[blctrl@areadetector exer]$ awk '$3<=20 {printf "%s\t%s\n",$0,"**";} $3>20 {print $0;}' food_list.txt
No Item_Name Quantity Price
1 Mangoes 45 $3.45
2 Apples 25 $2.45
3 Pineapples 5 $4.45 **
4 Tomatoes 25 $3.45
5 Onions 15 $1.45 **
6 Bananas 30 $3.45
在以上示例中,发生了两件重要事情:
1) 第一个表达式{action;}组合,$3<=30 {printf "%s\t%s\n",$0,"**";}打印出了数量小于等于30的行并且在每行末尾添加了一个(**)。使用$3字段变量访问数量的值。
2) 第二个表达式{action;}组合,$3>30 {print $0;},由于这些行的数量大于30,打印出不变化的行。
再一个例子:
[blctrl@areadetector exer]$ awk '$3<=20 {printf "%s\t%s\n",$0,"TRUE";} $3>20 {print $0;}' food_list.txt
No Item_Name Quantity Price
1 Mangoes 45 $3.45
2 Apples 25 $2.45
3 Pineapples 5 $4.45 TRUE
4 Tomatoes 25 $3.45
5 Onions 15 $1.45 TRUE
6 Bananas 30 $3.45
如何在Linux中与Awk一起使用'next'命令
在Awk系列这部分中,我们看一下使用next命令,其告诉Awk跳过你提供的所有余下的模式和表达式,而读取下一输入行。
next命令帮助你阻止执行我所称为在命令执行中浪费时间的步骤。
要理解它如何工作,让我使用一个food_list.txt的文件:
No Item_Name Price Quantity
1 Mangoes $3.45 5
2 Apples $2.45 25
3 Pineapples $4.45 55
4 Tomatoes $3.45 25
5 Onions $1.45 15
6 Bananas $3.45 30
思考一下以下命令行,它将在行尾用(*)符号标记出数量少于等于20的食物项:
[blctrl@areadetector exer]$ awk '$4<=20 {printf "%s\t%s\n",$0,"*";} $4>20 {print $0;}' food_list.txt
No Item_Name Price Quantity
1 Mangoes $3.45 5 *
2 Apples $2.45 25
3 Pineapples $4.45 55
4 Tomatoes $3.45 25
5 Onions $1.45 15 *
6 Bananas $3.45 30
以上命令实际运行如下:
1) 首先,它检查数量,每输入行的第四字段是否小于等于20,如果一个值满足了此条件,它使用表达式$4<=20,打印并且在末尾用(*)标记它。
2) 然后,它检查每输入行的第四个字段是否大于20,并且如果一行满足条件,它使用表达式2 $2>20打印它。
但这里有一个问题,当执行了第一个表达式时,使用{printf "%s\t%s\n", $0, "**";}打印了我们想要标记的行,并且接着在相同步骤中,第二个表达式也被检查,这变成了一个浪费时间的因素。所以,在使用第一个表达式已经打印了标记行后,不需要再执行第二个表达式$4>20。
要解决这个问题,你必须使用next命令:
[blctrl@areadetector exer]$ awk '$4<=20 {printf "%s\t%s\n",$0,"*";next;} $4>20 {print $0;}' food_list.txt
No Item_Name Price Quantity
1 Mangoes $3.45 5 *
2 Apples $2.45 25
3 Pineapples $4.45 55
4 Tomatoes $3.45 25
5 Onions $1.45 15 *
6 Bananas $3.45 30
在使用$4<=20 {printf "%s\t%s\n", $0,"*"; next;}打印一行后,,包含的next命令将跳过第二个表达式$4>20 {print $0;},因而执行进入下一输入行,而没有在检查数量是否大于20上浪费时间。
如何在Linux中从STDIN读取Awk输入
在Awk系列这部分,我们看一下一些示例:你能够在这些示例中从其它命令的输出过滤而不是从一个文件读取输入。
我们从dir工具开始,其工作类似ls命令,在以下第一个示例中,我们使用dir -l命令的输出作为Awk的输入来打印当前目录中所有者有用户名,组名和文件。
[blctrl@areadetector exer]$ dir -l | awk '{print $3,$4,$9;}'
blctrl blctrl food_list.txt
root blctrl food_list.txt.bak
我们通过在awk命令中使用一个表达式来过滤字符串,打印出由root用户所有的文件。
[blctrl@areadetector exer]$ dir -l | awk '$3=="root" {print $1,$3,$4,$9;}'
-rw-rw-r--. root blctrl food_list.txt.bak
以上命令包括(==)比较操作符来帮助我们过滤出在当前目录中由root用户所有的文件。使用$3="root"表达式实现这个目标。
让我们看一下另一个示例:我们在这个示例中使用awk比较操作符匹配特定字符串。
我们使用cat工具查看一个名称food_deals.txt文件,我们只想要查看类型为Fruit的食物项,所以我们应该运行以下命令:
[blctrl@areadetector exer]$ cat food_list.txt
No Item_Name Price Quantity Type
1 Mangoes $3.45 5 Fruit
2 Apples $2.45 25 Fruit
3 Pineapples $4.45 55 Fruit
4 Tomatoes $3.45 25 Vegetable
5 Onions $1.45 15 Vegetable
6 Bananas $3.45 30 Fruit
[blctrl@areadetector exer]$ cat food_list.txt | awk '$5 ~/Fruit/{print;}'
1 Mangoes $3.45 5 Fruit
2 Apples $2.45 25 Fruit
3 Pineapples $4.45 55 Fruit
6 Bananas $3.45 30 Fruit
在以上示例中,我们使用了~ /pattern/比较操作符,当使用这个比较操作符时要仔细,它是区分大小写的。