总结了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/passwdor写成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度= |
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()}'
求职群资源共享
手里资源比较多,字节、百度、京东、微软、阿里、商汤、携程等
历史资源都会同步归档在[夏普通的资源圈]知识星球里
- 每日分享求职日报
- 机会多多,各种一手内推资源
- 求职经验,面试总结,技术干货等资料共享
#面经##内推##春招##面试流程##秋招##字节跳动#下一篇将放出面经实例的代码详解。
感谢您的关注,欢迎交流。
腾讯云智研发成长空间 5048人发布