总结了200篇面经中的awk面试题,看面试官会问什么—原理篇
下一篇放问题回答与解析!!!!下一篇放问题回答与解析!!!!下一篇放问题回答与解析!!!!下一篇放问题回答与解析!!!!下一篇放问题回答与解析!!!!
先来看看涉及awk面经的词云统计:
花哨吧,据不完全统计,我了解的公司和岗位,哎嘿,它都有。
翻了大概200多篇面经,有一半都说不知道不了解awk,整理了一下其中涉及awk的问题,以及具体实例。本篇先来讲一下基本原理,下一篇将放出实例的详解!!建议电脑端阅读。
夏普通
原文
面经问题汇总
-- 询问篇
-- 实例篇
---- 文本处理
---- 去重统计排序
---- 查进程,kill指定进程
---- ip地址相关
---- 日志处理
日志概述&awk简介
-- awk的基本语法
-- 基本格式
-- 常用命令选项[options]
参数——-F|-f|-v
-- BEGIN
和END
——特殊pattern
-- 省略
-- 多个action&多个pattern{action}
awk常用内置变量
awk内置函数
-- 内置字符串函数
-- 内置时间函数
-- 内置数值函数
面经问题汇总
询问篇
实例篇
涉及文本处理、去重统计排序、统计词频、查进程kill指定进程、ip地址相关、日志处理等等
文本处理
去重统计排序
查进程,kill指定进程
ip地址相关
日志处理
日志概述&awk简介
linux下日志处理与分析在开发工作中非常重要。日志就是记录程序日常运行的信息。在调试代码、定位bug等方面发挥重要作用。开发日志很考验一个开发人员的能力和思维,这是一个逻辑性极强的项目。如果运行产生错误,能通过日志分析很快定位到问题,那可以说这是一个很不错的日志。因此对日志的设计也很重要,大大提高了定位bug的效率。(关于日志,暂且讲这么多,收!)
awk是一个强大的文本处理工具,非常优秀的数据过滤器。它不仅仅是一个工具,还是一门语言。命名来源于三个发明者姓氏的首字母组合(Aho、Weinberger、Kernighan)。
awk是行处理器,awk是以文件的一行为处理单位的。基于模式匹配依次对每一行文本进行处理,然后输出。如果数据规整,基于行(记录,record),也能很好地对列(字段,field)进行处理。
awk的基本语法
基本格式
格式1:awk [options] 'pattern{action}' filename 格式2:前置命令 | awk [options] 'pattern{action}' 格式3:awk [options] 'pattern{action}' 按回车之后,每输入一行文本,awk会处理一次并输出结果 注意: 1、awk后面必须是单引号,单引号里的内容(如字符串)用双引号,不然会导致引号匹配错误。 2、多个pattern{action}用;分号隔开。 3、多个{action;action;...}用;分号隔开。 4、处理文本时,若未指定分隔符,则默认将空格、制表符作为分隔符。
其中[options]
代表参数,单引号中的内容代表依据匹配条件(pattern)要执行的命令(action),filename
代表需要处理的文件。
管道符
|
对linux命令的输出结果进行再次处理,就可以使用管道符+管道命令
ps命令可以查看系统中的进程,但如果需要查看指定进程,就需要在ps命令返回的结果中进行筛选,如查看java进程:
ps -aux |grep java
凡是被{}
包裹的, 就是action;
凡是没有被{}
包裹的, 就是pattern。
输入如何被分解为记录(records,一条记录就是一行)由记录分隔符变量RS
(record separator variable)确定。默认情况下,RS="\n"
(换行符)。
将每个记录与每个pattern进行比较,如果匹配,则执行{action}的程序文本。
常用命令选项[options]
参数——-F|-f|-v
参数 | 说明 |
---|---|
-F value | 指定value (可以是字符串或正则表达式)为字段分隔符 |
-f scripfile.awk | 从脚本文件中读取awk命令,允许使用多个-f 选项 |
-v var=value | 为程序变量var 赋值,将外部变量传递给awk |
面试时的重中之重是-F
参数。
-F
参数:指定分隔符,可指定一个或多个,可省略(默认空格或Tab制表符)。
指定一个分隔符,例如
-F:
表示指定:
为分隔符;例如-F" "
表示指定空格为分隔符;例如-F"\n"
表示指定换行符为分隔符;指定多个分隔符,要加中括号
[]
,例如-F[,/]
表示指定,
和/
为分隔符;若未指定分隔符,则默认将 空格、制表符 作为分隔符(若指定了,则不默认,例如
-F"\t"
表示仅指定Tab制表符分隔符)。特殊的空格:指定空格为分隔符时,一个或多个连续的空格看做一个空格。
例如test.txt文本内容为111 222 333 444 awk -F" " '{print $4}' test.txt #输出为 444
如何指定连续的字符为分隔符,使用正则表达式+,+表示匹配一个字符出现1次或多次。
使用冒号举例,文本里可能出现连续几个冒号分割文本的情况 例如test.txt文本内容为123:456::789 awk -F: '{print $4}' test.txt #输出为 789 这里表示一个冒号为分隔符,第三列就是空值,第四列才是789 awk -F:+ '{print $3}' test.txt #输出为 789 这里:+表示连续一个或多个冒号为分隔符,第三列就是789 同样的,指定多个分隔符,要在中括号外加一个+号 -F[" ":,"\t"]+就表示同时用一个或多个空格、冒号、逗号、制表符作为分隔符
-F:
、-F,
可以写成-F":"
、-F","
;
但是-F"\t"
、-F"\n"
、-F" "
不可以写成-F\t
、-F\n
、-F
(转义字符、空格等容易存在歧义)。
BEGIN
和END
——特殊pattern
这是经常用到的两个关键字。BEGIN
用来说明在开始读入行之前的行为,END
用来说明处理完行之后的行为。
awk 'BEGIN{action} pattern{action} END{action}' filename
开始块(BEGIN
)在awk处理文本之前执行,并在整个过程中只执行一次。用来预处理(例如初始化FS
字段分隔符变量、初始化一些全局变量)或打印表头等。可以没有BEGIN
部分。
结束块(END
)在程序结束时执行,主要是进行数据汇总或者打印结尾信息等。可以没有END
部分。
下面是需要绕一绕逻辑的东西!(后面代码演示的时候怕大家不理解为什么要那么写,这里先讲一下)
开始块(BEGIN
):在读取filename文件之前。awk会执行一次BEGIN
块后面{}里的一个或多个用;
隔开的action(s)。
中间主体pattern{action}部分,可以没有。但如果有的话,必须要有前置命令
或filename
或按回车之后,每输入一行文本,awk会处理一次并输出结果
。
结束块(END
): 当filename的文档被上方pattern处理完毕后,awk会执行END
后面{}里的一个或多个用;
隔开的action(s)。
【如何理解,用后面会讲到的内置时间函数systime()来举例说明】:
#systime()返回当前时间距离Epoch纪元时间(1970-01-01 00:00:00 UTC)有多少秒 awk '{print systime()}' 按回车之后发现必须输入任意文本{print systime()}才能被执行
在没有filename的情况下,有两种方式呈现出来:
1、把action写进BEGIN
后面{}里
awk 'BEGIN{print systime()}'
2、写前置命令echo 123
,通过管道送入awk
echo 123|awk '{print systime()}' # echo 123,就类似于上面所说,回车之后输入123,再回车看到结果了
Linux中echo命令可以输出内容到控制台
其实写任意前置命令都可以实现,但需要注的是,前置命令产生的输出有几行,后面awk的指令就会被执行几次。
ps -aux|awk '{print systime()}' !!!只是为了演示,现实情况下,肯定不这么奇怪的写代码
ps显示系统进程,-aux查看所有进程详细信息(静态的看)
ps -aux
总结BEGIN
和END
下的awk工作流程:
- 1、在filename文件读取之前,先执行
BEGIN
块后面{}里的action - 2、然后一行一行读取filename中的记录,进行pattern的匹配
- 3、当满足pattern匹配条件,pattern后面{}里的action就会被执行
- 4、如果存在多组pattern(action),会一一进行匹配和处理
- 5、当所有pattern和所有filename中的行匹配并处理完毕后,
END
后面{}里的action将会被执行
省略
One, but not both, of pattern {action} can be omitted. If {action} is omitted it is implicitly {print}. If pattern is omitted, then it is implicitly matched. BEGIN and END patterns require an action.
可以省略pattern{action}中的一个,但不能同时省略两个。
如果省略了{action},它默认是{print}
打印。
如果pattern被省略,那么它没有匹配规则限制,匹配所有数据,执行{action}的指令。
特别地,在使用BEGIN
和END
块时,必须要有{action}。
多个action&多个pattern{action}
1、多个action
awk 'pattern{action;action;...}' filename awk 'BEGIN{action;action;...} pattern{action;action;...} END{action;action;...}' filename
多个action用;
分号隔开
2、多个pattern{action}
awk 'pattern{action};pattern{action};....' filename awk 'BEGIN{action} pattern{action};pattern{action};.... END{action}' filename
awk程序由一系列pattern以及与之对应的{action}组成,每组规则之间用;
分号隔开, 一条输入记录与pattern匹配则执行与之对于的{action}。
3、多个action&多个pattern{action}
awk 'pattern{action;action;...};pattern{action;action;...};....' filename awk 'BEGIN{action;action;...} pattern{action;action;...};pattern{action;action;...};.... END{action;action;...}' filename
awk常用内置变量
以下变量是内置的,并在程序执行前初始化。
N:number 编号、序号;
F:field 列、字段;
R:record 行、记录。
变量 | description |
---|---|
$1~$n | 当前处理行的第n个字段(第n列),字段间由FS分隔 |
$0 | 当前处理的行的整行信息 |
ARGC | 命令行参数的数量 |
ARGV | 一个数组(下标从0开始),存储命令行中执行的每个参数 |
FIELDWIDTHS | 字段宽度列表(用空格键分隔) |
FILENAME | 当前输入文件的名称 |
FS | Field Separator 指定每行文本的字段分隔符(列分隔符),默认为空格、Tab制表位 |
RS | Record Separator 输入记录(行)分隔符,默认为"\n",即每行为一条记录 |
NF | Number of Field 当前行的字段个数(当前行的列数) |
NR | Number of Record 当前行的编号(行号、从1开始),到目前为止的行数(会按照文件累加) |
FNR | File Number of Record 当前文件中的行号 |
OFS | Output Field Separator 输出字段(列)分隔符,在输出的字段之间插入OFS,默认为" "空格 |
ORS | Output Record Separator 输出记录(行)分隔符,在输出的记录之间插入ORS,默认为"\n"换行 |
在awk处理多个输入文件的时候,在处理完第一个文件后,NR
并不会从1开始,而是继续累加,因此就出现了FNR
。每当处理一个新文件的时候,FNR
就从1开始计数。
文件test.txt内容: abc@123:1990 def@456::1987 ghi@789,2000 awk 'BEGIN{FS="@"}{print "当前行号:" NR "\t" "当前行字段个数:" NF "\t" $1 "\t" $2}' test.txt # 当前行号:1 当前行字段个数:2 abc 123#1990 # 当前行号:2 当前行字段个数:2 def 456::1987 # 当前行号:3 当前行字段个数:2 ghi 789,2000 awk 'BEGIN{RS="[@:]+"}{print $0}' test.txt awk -v RS="[@:]+" '{print $0}' test.txt # abc # 123#1990 # def # 456 # 1987 # ghi # 789,2000 awk 'BEGIN{FS="@";ORS="****"}{print $0}' test.txt # abc@123#1990****def@456::1987****ghi@789,2000****
awk内置函数
内置字符串函数
写在前面,gsub(r,s [,t])
中[]
表示参数可选。
也就是说函数可以写成gsub(r,s,t)
或gsub(r,s)
。
(regular expression,subsitution string,target string)简称(r,s,t)。
gsub(r,s [,t])
全局替换,字符串t
中所有与正则表达式r
匹配的项都被替换为字符串s
。函数返回值为替换的数量。如果省略t
,则默认对$0
进行替换。
head -n 1 /etc/passwd | awk '{gsub("root","**");print $0}' #输出为 **:x:0:0:**:/**:/bin/bash head -n 1 /etc/passwd | awk '{print(gsub("root","**"))}' #输出为 3 #函数返回值为替换的数量
拓展
/etc/passwd这个⽂件中保存的就是系统中所有的⽤户和⽤户的主要信息。
head命令可用于查看文件的开头部分的内容,有一个常用的参数 -n 用于显示行数,默认为10,即显示10 行的内容。
head -n 1 /etc/passwd
or写成head -1 /etc/passwd
表示显示/etc/passwd这个⽂件中的第一行输出结果为
root:x:0:0:root:/root:/bin/bash
sub(r,s [,t])
单次替换,与gsub类似,但最多只能替换一次。仅替换第一个匹配的字符串,而不是替换全部。
head -n 1 /etc/passwd | awk '{sub("root","**");print $0}' #输出为 **:x:0:0:root:/root:/bin/bash #只有第一个root被替换了 head -n 1 /etc/passwd | awk '{print(sub("root","**"))}' #输出为 1
sub和gsub的区别:
sub,substitute,替换
gsub,global substitute,全局替换
sub匹配第一次出现的符合模式的字符串
gsub匹配所有的符合模式的字符串
gsub返回的是替换的次数
substr(s,i [,n])
返回长度为n
的字符串s
的子字符串。从索引i
开始(字符串s
第一个字符的索引为1),对字符串s
进行截取,截取n
个字符。如果省略n,则返回s
的后缀(即从索引i
开始一直截取到字符串s的末尾)。
#从第2位开始截取3个字符 awk 'BEGIN{xpt="Hello xpt"; print substr(xpt,7,3)}' #输出为 xpt
index(s,t)
如果字符串t
是s
的子串,则返回t
开始的索引,否则返回0。(字符串s
第一个字符的索引为1)。
awk 'BEGIN{xpt="Hello xpt"; print index(xpt,"hello")}' #输出为 0 awk 'BEGIN{xpt="Hello xpt"; print index(xpt,"Hello")}' #输出为 1
length(s)
返回字符串或数组的长度,如果不指定s
则统计$0
的长度。
#返回字符串长度 awk 'BEGIN{xpt="Hello xpt"; print length(xpt)}' #输出为 9 #返回数组元素个数 awk 'BEGIN{xpt[0]="Hello";xpt[1]="xpt"; print length(xpt)}' #输出为 2 head -1 /etc/passwd |awk '{print length()}' #输出为 31
match(s,r)
返回字符串s
中与正则表达式r
的第一个匹配项的索引。如果不匹配,则返回0。(字符串s
第一个字符的索引为1)。
awk 'BEGIN{xpt="Hello xpt";print match(xpt,"[a-z]")}' #输出为 2 #小写字母在第2个位置开始出现
split(s,A [,r])
split(字符串,数组,分隔符)
字符串s
被正则表达式r
拆分为多个字段,存储在数组A
中。函数返回值为字段数目。如果省略r
,使用FS
输入字段分隔符。(数组下标从1开始)
awk 'BEGIN{split("Hello! xpt",arr,"!"); print arr[1],arr[2]}' #输出为 Hello xpt awk 'BEGIN{split("Hello! xpt",arr); print arr[1]}' #输出为 Hello!
tolower(s)
可以将字符串s
中所有大写字符转换为小写。
awk 'BEGIN{xpt="Hello xpt";print tolower(xpt)}' #输出为 hello xpt
toupper(s)
可以将字符串s
中所有小写字符转换为大写。
awk 'BEGIN{xpt="Hello xpt";print toupper(xpt)}' #输出为 HELLO XPT
sprintf(format,expr-list)
返回根据format格式化从expr-list构造的字符串。
#使用%3.2f指定保留2位小数 seq 5 | awk '{print sprintf("%3.2f",$0/2)}' #输出为 0.50 1.00 1.50 2.00 2.50
seq命令用于产生从某个数到另外一个数之间的所有整数
内置时间函数
systime()
返回当前时间距离Epoch纪元时间(1970-01-01 00:00:00 UTC)有多少秒
#当前时间2022-05-04 01:10:08 awk 'BEGIN{print systime()}' #输出为 1651597808
strftime([format [,timestamp [,utc ]]])
strftime([格式[,时间戳[,utc]]])
使用format格式化时间戳。格式化形式举例:
格式 | description |
---|---|
%Y | 四位数年份(如2022) |
%y | 2位数的年份(2022年对应22年) |
%C | 世纪数(2022年对应21世纪) |
%m | 月份(01-12) |
%d | 月份中的某一天(01-31) |
%H | 24小时制的小时数(00–23) |
%I | 12小时制的小时数(01–12) |
%p | 显示12小时制的AM/PM |
%M | 分钟数(00–59) |
%S | 秒数(00–60) |
%s | 距离Epoch纪元时间有多少秒(和systime() 结果一样) |
%j | 年份中的某一天(001–366) |
%A | 星期几的英文全称,如Monday、Tuesday、Wednesday |
%a | 星期几的缩写,如Mon、Tue、Wed |
%B | 月份的英文全称,如May、September、October |
%b | 月份的英文缩写,如May、Sep、Oct |
%c | 本地的日期和时间(如Wed May 4 01:10:08 2022 ) |
%W | 一年中第几周(如2022-05-04是第18周) |
- 如果缺少格式参数,则默认为"%c"。
- 如果缺少时间戳参数,则使用systime的当前值。
- 如果utc参数存在且不为零,则结果为utc。否则使用当地时间。
echo 123 | awk '{print strftime ("%Y-%m-%d %H:%M:%S",1651597808)}' #输出为 2022-05-04 01:10:08 #如果缺少格式参数,则默认为"%c" echo 123 | awk '{print strftime ()}' #输出为 Wed May 4 01:16:55 2022 #如果缺少时间戳参数,则使用systime的当前值 echo 123 | awk '{print strftime ("%Y-%m-%d %H:%M:%S")}' #输出为 2022-05-04 01:13:31
mktime(specification)
mktime("YYYY mm DD HH MM SS [DST]")
注意必须使用空格分割,将日期转换为与systime()
具有相同单位的时间戳。返回距离Epoch纪元时间有多少秒,失败则返回-1。
echo 123 | awk '{print "2022-05-04 01:10:08的时间戳为:" mktime("2022 05 04 01 10 08")}' #输出为 2022-05-04 01:10:08的时间戳为:1651597808
内置数值函数
数值函数 | description |
---|---|
int(x) | 将x 截断为整数 |
sqrt(x) | 返回x 的平方根 |
sin(x) | 正弦函数,x 以弧度表示 |
cos(x) | 余弦函数,x 以弧度表示 |
atan2(y,x) | 求y/x(弧度表示)的反正切值 |
exp(x) | |
log(x) | |
rand() | 返回一个[0,1) 之间的随机数 |
srand([expr]) | 用于重复伪随机序列。[expr] 参数提供一个种子,该种子对应一个随机数,如果使用相同的种子后面的rand() 函数会出现一样的随机数。如果省略expr,则使用当前时间的epoch值为随机数生成器种子。 |
弧度制与角度制的换算公式 | 1度=/180≈0.01745弧度 |
awk 'BEGIN{print int(12.23)}' #输出为 12 awk 'BEGIN{print sqrt(4)}' #输出为 2 awk 'BEGIN{print sin(0.52359879)}' #输出为 0.5 #30 度=0.52359879 弧度 awk 'BEGIN{print cos(1.0471976)}' #输出为 0.5 #60 度=1.0471976 弧度 awk 'BEGIN{print atan2(0.5,0.5)}' #输出为 0.785398 awk 'BEGIN{print log(exp(2))}' #输出为 2 awk 'BEGIN{print exp(1)}' #输出为 2.71828 awk 'BEGIN{print rand()}' #输出为 0.486681 awk 'BEGIN{print srand();print systime()}' #输出为 1651653977 1651653977 awk 'BEGIN{print srand(1);print systime()}'
求职群资源共享
手里资源比较多,字节、百度、京东、微软、阿里、商汤、携程等
历史资源都会同步归档在[夏普通的资源圈]知识星球里
- 每日分享求职日报
- 机会多多,各种一手内推资源
- 求职经验,面试总结,技术干货等资料共享
#面经##内推##春招##面试流程##秋招##字节跳动#下一篇将放出面经实例的代码详解。
感谢您的关注,欢迎交流。