总结了200篇面经中的awk面试题,看面试官会问什么—原理篇

下一篇放问题回答与解析!!!!下一篇放问题回答与解析!!!!下一篇放问题回答与解析!!!!下一篇放问题回答与解析!!!!下一篇放问题回答与解析!!!!

先来看看涉及awk面经的词云统计:
)
花哨吧,据不完全统计,我了解的公司和岗位,哎嘿,它都有。

翻了大概200多篇面经,有一半都说不知道不了解awk,整理了一下其中涉及awk的问题,以及具体实例。本篇先来讲一下基本原理,下一篇将放出实例的详解!!建议电脑端阅读。

夏普通

原文
面经问题汇总
-- 询问篇
-- 实例篇
---- 文本处理
---- 去重统计排序
---- 查进程,kill指定进程
---- ip地址相关
---- 日志处理
日志概述&awk简介
-- awk的基本语法
-- 基本格式
-- 常用命令选项[options]参数——-F|-f|-v
-- BEGINEND——特殊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(转义字符、空格等容易存在歧义)。

BEGINEND——特殊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

总结BEGINEND下的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}的指令。

特别地,在使用BEGINEND块时,必须要有{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)

如果字符串ts的子串,则返回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()}'

图片说明

求职群资源共享

手里资源比较多,字节、百度、京东、微软、阿里、商汤、携程等

历史资源都会同步归档在[夏普通的资源圈]知识星球里

  • 每日分享求职日报
  • 机会多多,各种一手内推资源
  • 求职经验,面试总结,技术干货等资料共享

在这里插入图片描述

下一篇将放出面经实例的代码详解。

感谢您的关注,欢迎交流。

#面经##内推##春招##面试流程##秋招##字节跳动#
全部评论
总觉得awk很复杂
点赞 回复 分享
发布于 2022-05-06 17:55

相关推荐

2024-12-04 23:27
已编辑
赣南科技学院 Java
抱抱碍事梨a:你这项目时间挺有意思,写完直接年轻了七岁,下次有这种好项目多推给我几个
点赞 评论 收藏
分享
2024-12-01 23:36
信阳学院 Java
在拧螺丝的西红柿很热情:看发布昨天发的,昨天周天,前天周六,没谁回复你的,而且实习一般年前很少收人,一般年后或者暑假收
点赞 评论 收藏
分享
评论
6
20
分享

创作者周榜

更多
牛客网
牛客企业服务