awk
是一种强大的文本处理工具,在 Linux 和 Unix 系统中非常流行。它主要用于模式扫描和处理语言,可以读取输入文件、为数据行中的字段指定操作,并基于这些操作输出数据。awk
程序通常由一系列的模式和动作对组成,模式指定了哪些数据行应被处理,而动作则定义了在这些行上应执行的操作。
基本语法
bash
awk 'pattern { action }' input_file
用法:awk [options] '{print NR,$0}' file
-F 指定字段分隔符
NR 表示行号
$0 表示这一行的内容
$1 数字 某一列
$NF 最后一列
pattern
:是可选的,用于指定哪些行应该被处理。如果省略,则处理所有行。{ action }
:是必需的,定义了当行匹配模式时应该执行的操作。input_file
:指定输入文件名。如果省略,则从标准输入读取数据。
示例
打印所有行
bash
awk '{print}' filename
或简单地
bash
awk 1 filename
这里,1
被视为总是为真的模式,因此所有行都会被打印。
打印第一列
bash
awk '{print $1}' filename
$1
表示第一列。
打印第1~7列
bash
[root@localhost ~]# head -10 /etc/passwd | awk -F ":" '{print $1,$2,$3,$4,$5,$6,$7}'
root x 0 0 root /root /bin/bash
bin x 1 1 bin /bin /sbin/nologin
daemon x 2 2 daemon /sbin /sbin/nologin
adm x 3 4 adm /var/adm /sbin/nologin
lp x 4 7 lp /var/spool/lpd /sbin/nologin
sync x 5 0 sync /sbin /bin/sync
shutdown x 6 0 shutdown /sbin /sbin/shutdown
halt x 7 0 halt /sbin /sbin/halt
mail x 8 12 mail /var/spool/mail /sbin/nologin
operator x 11 0 operator /root /sbin/nologin
打印当前记录(即整行)的内容
bash
[root@localhost ~]# head -10 /etc/passwd | awk -F ":" '{print $0}'
root:x:0:0:root:/root:/bin/bash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
整个命令 head -10 /etc/passwd | awk -F ":" '{print $0}' 的效果其实与仅执行 head -10 /etc/passwd 相同,因为 awk 命令在这里并没有对每行文本进行任何实质性的处理(除了指定了字段分隔符,但这个分隔符在 print $0 的上下文中并未被利用)
bash
[root@localhost ~]# head -10 /etc/passwd | awk '{FS=":"}{print $1,$2,$3,$4,$5,$6,$7}'
root:x:0:0:root:/root:/bin/bash
bin x 1 1 bin /bin /sbin/nologin
daemon x 2 2 daemon /sbin /sbin/nologin
adm x 3 4 adm /var/adm /sbin/nologin
lp x 4 7 lp /var/spool/lpd /sbin/nologin
sync x 5 0 sync /sbin /bin/sync
shutdown x 6 0 shutdown /sbin /sbin/shutdown
halt x 7 0 halt /sbin /sbin/halt
mail x 8 12 mail /var/spool/mail /sbin/nologin
operator x 11 0 operator /root /sbin/nologin
在您提供的命令 head -10 /etc/passwd | awk '{FS=":"}{print $1,$2,$3,$4,$5,$6,$7}'
中,存在一个常见的 awk
使用误区。FS
(字段分隔符)应该在处理任何记录之前设置,而不是在每条记录的处理块中单独设置。当您在 {FS=":"}
这样的代码块中设置 FS
时,它实际上对已经读取到当前记录(即行)没有影响,因为 awk
在读取记录时就已经决定了如何根据 FS
分割这条记录。
因此,您的命令实际上不会按预期工作,因为它在尝试打印字段时并没有正确地使用冒号作为字段分隔符。
正确的做法是在 awk
程序开始时就设置 FS
,然后处理记录。这里是修改后的命令:
bash
head -10 /etc/passwd | awk -F: '{print $1, $2, $3, $4, $5, $6, $7}'
注意,我已经将 -F:
直接传递给了 awk
命令,而不是在 awk
程序内部设置 FS
。这样做可以在 awk
读取每行之前就将冒号设置为字段分隔符。
这个命令将输出 /etc/passwd
文件前10行中每行的前七个字段(如果存在的话)。这些字段通常包括用户名、密码占位符(通常是 x
,表示密码存储在 /etc/shadow
中)、用户ID(UID)、组ID(GID)、用户全名或注释、家目录和登录Shell。不过,请注意,并非所有用户的记录都会有七个字段,特别是如果用户全名或注释字段为空或不存在时。但是,awk
会按照指定的字段数打印出内容,对于不存在的字段,它会打印空字符串。
对每行的第一个字段求和
bash
awk '{sum += $1} END {print sum}' filename
这个命令会遍历文件中的每一行,将第一列的值累加到变量 sum
中,并在处理完所有行后打印总和。
过滤特定行
打印文件中第二列值大于 10 的所有行:
bash
awk '$2 > 10 {print}' filename
bash
[root@localhost ~]# vi score.txt
Marry 2143 78 84 77
Jack 2321 66 78 45
Tom 2122 48 77 71
Mike 2537 87 97 95
Bob 2415 40 57 62
Bigmao 8899 99 100 98
-- 计算总成绩shell角本
vi cat_call.awk
#!/bin/awk -f
#运行前
BEGIN {
math = 0
english = 0
computer = 0
printf "NAME NO. MATH ENGLISH COMPUTER TOTAL\n"
printf "---------------------------------------------\n"
}
#运行中
{
math+=$3
english+=$4
computer+=$5
printf "%-6s %-6s %4d %8d %8d %8d\n", $1, $2, $3,$4,$5, $3+$4+$5
}
#运行后
END {
printf "---------------------------------------------\n"
printf " TOTAL:%10d %8d %8d \n", math, english, computer
printf "AVERAGE:%10.2f %8.2f %8.2f\n", math/NR, english/NR, computer/NR
}
[root@localhost ~]# awk -f cat_call.awk score.txt
NAME NO. MATH ENGLISH COMPUTER TOTAL
---------------------------------------------
Marry 2143 78 84 77 239
Jack 2321 66 78 45 189
Tom 2122 48 77 71 196
Mike 2537 87 97 95 279
Bob 2415 40 57 62 159
Bigmao 8899 99 100 98 297
---------------------------------------------
TOTAL: 418 493 448
AVERAGE: 69.67 82.17 74.67
使用 BEGIN 和 END 块
bash
awk 'BEGIN {print "Start processing file..."} {print} END {print "File processing complete."}' filename
BEGIN
块在处理任何输入行之前执行,而 END
块在所有输入行处理完毕后执行。
bash
[root@localhost ~]# head -10 /etc/passwd | awk 'BEGIN{FS=":";OFS="****"}{print $1,$2,$3,$4,$5,$6,$7}'
root****x****0****0****root****/root****/bin/bash
bin****x****1****1****bin****/bin****/sbin/nologin
daemon****x****2****2****daemon****/sbin****/sbin/nologin
adm****x****3****4****adm****/var/adm****/sbin/nologin
lp****x****4****7****lp****/var/spool/lpd****/sbin/nologin
sync****x****5****0****sync****/sbin****/bin/sync
shutdown****x****6****0****shutdown****/sbin****/sbin/shutdown
halt****x****7****0****halt****/sbin****/sbin/halt
mail****x****8****12****mail****/var/spool/mail****/sbin/nologin
operator****x****11****0****operator****/root****/sbin/nologin
[root@localhost ~]# head -10 /etc/passwd | awk 'BEGIN{FS=":"}{print $NF}'
/bin/bash
/sbin/nologin
/sbin/nologin
/sbin/nologin
/sbin/nologin
/bin/sync
/sbin/shutdown
/sbin/halt
/sbin/nologin
/sbin/nologin
使用外部变量
bash
awk -v var=100 '{if ($1 > var) print $0}' filename
这里,-v var=100
定义了一个名为 var
的外部变量,并将其值设置为 100。然后,在 awk
程序内部使用这个变量来比较第一列的值。
进阶用法
awk
还可以进行更复杂的文本处理,如字符串操作、数组使用、自定义函数等。这些功能使得 awk
成为处理文本数据的强大工具。
注意事项
- 字段默认由空格或制表符分隔。可以通过
-F
选项更改字段分隔符。 awk
程序中的{ action }
可以包含多个语句,语句之间用分号分隔。- 变量名区分大小写。
awk
的功能远不止于此,通过结合其内置函数和灵活的语法,你可以完成几乎任何文本处理任务。