【Shell牛客刷题系列】SHELL28 nginx日志分析6-统计每分钟的请求数

2023-11-06



该系列是基于牛客Shell题库,针对具体题目进行查漏补缺,学习相应的命令。

刷题链接:牛客题霸-Shell篇

该系列文章都放到专栏下,专栏链接为:《专栏:Shell》。欢迎关注专栏~

本文知识预告:

  • 本文首先学习了sprintf()函数的用法,然后复习了sortawkuniq等命令的相关用法;
  • 最后,给出了四种题目的解决方案。


SHELL28 nginx日志分析6-统计每分钟的请求数

假设nginx的日志存储在nowcoder.txt里,内容如下:

192.168.1.20 - - [21/Apr/2020:14:12:49 +0800] "GET /1/index.php HTTP/1.1" 404 490 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:45.0) Gecko/20100101 Firefox/45.0"
192.168.1.21 - - [21/Apr/2020:15:00:49 +0800] "GET /2/index.php HTTP/1.1" 404 490 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:45.0) Gecko/20100101 Firefox/45.0"
192.168.1.22 - - [21/Apr/2020:21:21:49 +0800] "GET /3/index.php HTTP/1.1" 404 490 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:45.0) Gecko/20100101 Firefox/45.0"
192.168.1.23 - - [21/Apr/2020:22:10:49 +0800] "GET /1/index.php HTTP/1.1" 404 490 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:45.0) Gecko/20100101 Firefox/45.0"
192.168.1.24 - - [22/Apr/2020:15:00:49 +0800] "GET /2/index.php HTTP/1.1" 404 490 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:45.0) Gecko/20100101 Firefox/45.0"
192.168.1.25 - - [22/Apr/2020:15:26:49 +0800] "GET /3/index.php HTTP/1.1" 404 490 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:45.0) Gecko/20100101 Firefox/45.0"
192.168.1.20 - - [23/Apr/2020:08:05:49 +0800] "GET /2/index.php HTTP/1.1" 404 490 "-" "Baiduspider"
192.168.1.21 - - [23/Apr/2020:09:20:49 +0800] "GET /1/index.php HTTP/1.1" 404 490 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:45.0) Gecko/20100101 Firefox/45.0"
192.168.1.22 - - [23/Apr/2020:10:27:49 +0800] "GET /1/index.php HTTP/1.1" 404 490 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:45.0) Gecko/20100101 Firefox/45.0"
192.168.1.22 - - [23/Apr/2020:10:27:49 +0800] "GET /1/index.php HTTP/1.1" 404 490 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:45.0) Gecko/20100101 Firefox/45.0"
192.168.1.20 - - [23/Apr/2020:14:12:49 +0800] "GET /1/index.php HTTP/1.1" 404 490 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:45.0) Gecko/20100101 Firefox/45.0"
192.168.1.21 - - [23/Apr/2020:15:00:49 +0800] "GET /2/index.php HTTP/1.1" 404 490 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:45.0) Gecko/20100101 Firefox/45.0"
192.168.1.22 - - [23/Apr/2020:15:00:49 +0800] "GET /3/index.php HTTP/1.1" 404 490 "-" "Baiduspider"
192.168.1.25 - - [23/Apr/2020:16:15:49 +0800] "GET /1/index.php HTTP/1.1" 404 490 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:45.0) Gecko/20100101 Firefox/45.0"
192.168.1.24 - - [23/Apr/2020:20:27:49 +0800] "GET /2/index.php HTTP/1.1" 404 490 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:45.0) Gecko/20100101 Firefox/45.0"
192.168.1.25 - - [23/Apr/2020:20:27:49 +0800] "GET /3/index.php HTTP/1.1" 404 490 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:45.0) Gecko/20100101 Firefox/45.0"
192.168.1.20 - - [23/Apr/2020:20:27:49 +0800] "GET /1/index.php HTTP/1.1" 404 490 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:45.0) Gecko/20100101 Firefox/45.0"
192.168.1.21 - - [23/Apr/2020:20:27:49 +0800] "GET /1/index.php HTTP/1.1" 404 490 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:45.0) Gecko/20100101 Firefox/45.0"
192.168.1.22 - - [23/Apr/2020:20:27:49 +0800] "GET /1/index.php HTTP/1.1" 404 490 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:45.0) Gecko/20100101 Firefox/45.0"
192.168.1.22 - - [23/Apr/2020:22:10:49 +0800] "GET /1/index.php HTTP/1.1" 404 490 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:45.0) Gecko/20100101 Firefox/45.0"
192.168.1.21 - - [23/Apr/2020:23:59:49 +0800] "GET /1/index.php HTTP/1.1" 404 490 "-" "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:45.0) Gecko/20100101 Firefox/45.0"

现在需要编写shell脚本统计每分钟的请求数,并且按照请求数降序排序。你的脚本应该输出:

5 20:27
4 15:00
2 22:10
2 14:12
2 10:27
1 23:59
1 21:21
1 16:15
1 15:26
1 09:20
1 08:05

相关命令学习

sprintf()

sprintfprintf 在用法上几乎一样,只是打印的目的地不同而已,前者打印到字符串中,后者则直接在命令行上输出。这也导致 sprintfprintf 有用得多。

sprintf是个变参函数,定义如下:

int sprintf( char *buffer, const char *format [, argument]);

除了前两个参数类型固定外,后面可以接任意多个参数。而它的精华,显然就在第二个参数:格式化字符串上。

printfsprintf 都使用格式化字符串来指定串的格式,在格式串内部使用一些以“%”开头的格式说明符(format specifications)来占据一个位置,在后边的变参列表中提供相应的变量,最终函数就会用相应位置的变量来替代那个说明符,产生一个调用者想要 的字符串。

格式化数字字符串

  1. 把整数123 打印成一个字符串保存在s 中。
sprintf(s, "%d", 123)   # 产生"123"
sprintf(s, "%8d%8d", 123, 4567); // 产生:" 123 4567"当然也可以左对齐:
sprintf(s, "%-8d%8d", 123, 4567); // 产生:"123 4567"也可以按照16进制打印:
  1. 可以指定宽度,不足的左边补空格:
sprintf(s, "%8d%8d", 123, 4567)  # 产生:" 123 4567"
  1. 当然也可以左对齐:
sprintf(s, "%-8d%8d", 123, 4567)  #  产生:"123 4567"
  1. 也可以按照16进制打印:
sprintf(s, "%8x", 4567)  # 小写16进制,宽度占8个位置

控制浮点数打印格式

  1. 浮点数的打印和格式控制是 sprintf 的又一大常用功能,浮点数使用格式符“%f”控制,默认保留小数点后6位数字,比如:
sprintf(s, "%f", 3.1415926)    # 产生 "3.141593"
  1. 使用%m.nf格式控制打印的宽度和小数位数,其中m表示打印的宽度,n表示小数点后的位数。比如:
sprintf(s, "%10.3f", 3.1415626)    #  产生:" 3.142"
sprintf(s, "%-10.3f", 3.1415626)   # 产生:"3.142 "
sprintf(s, "%.3f", 3.1415626);     # 不指定总宽度

使用 sprintf 的常见问题

sprintf是个变参函数,使用时经常出问题,而且只要出问题通常就是能导致程序崩溃的内存访问错误,但好在由sprintf误用导致的问题虽然严重,却很容易找出,无非就是那么几种情况,通常用眼睛再把出错的代码多看几眼就看出来了。

  • 缓冲区溢出:第一个参数的长度太短了,当然也可能是后面的参数的问题,建议变参对应一定要细心,而打印字符串时,尽量指定最大字符数。
  • 忘记了第一个参数:低级得不能再低级问题,用 printf 用得太惯了。
  • 变参对应出问题:通常是忘记了提供对应某个格式符的变参,导致以后的参数统统错位

awk:文本和数据进行处理的编程语言

awk命令来自于三位创始人”Alfred Aho,Peter Weinberger, Brian Kernighan “的姓氏缩写,其功能是用于对文本和数据进行处理的编程语言。使用awk命令可以让用户自定义函数或正则表达式对文本内容进行高效管理,与sedgrep并称为Linux系统中的文本三剑客。

语法格式awk 参数 文件

常用参数

参数 功能
-F 指定输入时用到的字段分隔符
-v 自定义变量
-f 从脚本中读取awk命令
-m val值设置内在限制

常用的awk内置变量

awk语法由一系列条件和动作组成,在花括号内可以有多个动作,多个动作之间用分号分隔,在多个条件和动作之间可以有若干空格,也可以没有。

变量名称 说明
FILENAME 当前输入文档的文件名
FNR 当前输入文档的当前行号,尤其当多个输入文档时有用
FS 设置字段分隔符,默认为空格或制表符
NF 当前记录(行)的字段(列)个数
NR 输入数据流的当前记录数(行号)
OFS 输出字段分隔符,默认为空格
ORS 输出记录分隔符,默认为换行符
RS 输入记录分隔符,默认为换行符

awk是一种处理文本文件的编程语言,文件的每行数据都被称为记录默认以空格或制表符为分隔符每条记录被分成若干字段(列)awk每次从文件中读取一条记录

sort:对文件内容进行排序

sort命令的功能是对文件内容进行排序。有时文本中的内容顺序不正确,一行行地手动修改实在太麻烦了。此时使用sort命令就再合适不过了,它能够对文本内容进行再次排序。

语法格式:sort [参数] 文件

常用参数:

-b 忽略每行前面开始出的空格字符
-c 检查文件是否已经按照顺序排序
-d 除字母、数字及空格字符外,忽略其他字符
-f 将小写字母视为大写字母
-i 除040至176之间的ASCII字符外,忽略其他字符
-m 将几个排序号的文件进行合并
-M 将前面3个字母依照月份的缩写进行排序
-n 依照数值的大小排序
-o <输出文件> 将排序后的结果存入制定的文件
-r 以相反的顺序来排序
-t <分隔字符> 指定排序时所用的栏位分隔字符
-k 指定需要排序的栏位
-s 通过禁用最后的比较来稳定排序

substr() :截取子串

第一种方法substr()配合awk命令

其语法格式:(开始索引以0或1开始)

substr(源字符串, 开始索引, 长度) 

例子:

lucky@DESKTOP-VQ8KID4:~$ cat nowcoder.txt
a12b8
10ccc
2521abc
lucky@DESKTOP-VQ8KID4:~$ awk -F" " '{print substr($1,2,3)}' nowcoder.txt
12b
0cc
521

正如上面提到的,开始索引以1开始和从0开始都一样:

lucky@DESKTOP-VQ8KID4:~$ awk -F" " '{print substr($1,0,3)}' nowcoder.txt
a12
10c
252
lucky@DESKTOP-VQ8KID4:~$ awk -F" " '{print substr($1,1,3)}' nowcoder.txt
a12
10c
252

第二种方法substr()配合expr命令

其语法格式为:(开始索引以1开始)

expr substr 字符串 开始索引 长度

例子:

lucky@DESKTOP-VQ8KID4:~$ expr substr "sdfsdf" 2 2
df

第三种方法:可以根据特定字符偏移和长度,使用另一种形式的变量扩展,来选择特定子字符串。【开始索引为0】

其语法格式为:

  1. 截取变量从n1到最后的字符串,其语法格式为:(只提供一个参数的话,默认截取到最后)
echo ${str:n1}
  1. 截取变量从n1n2之间的字符串。
echo ${str:n1:n2-n1}

uniq:去除文件中的重复内容行

uniq命令来自于英文单词unique的缩写,中文译为独特的、唯一的,其功能是用于去除文件中的重复内容行uniq命令能够去除掉文件中相邻的重复内容行,如果两端相同内容中间夹杂了其他文本行,则需要先使用sort命令进行排序后再去重复,这样保留下来的内容就都是唯一的了。

划重点:去除相邻重复内容行!

语法格式:uniq [参数] 文件

常用参数:

-c 打印每行在文本中重复出现的次数
-d 每个重复纪录只出现一次
-u 只显示没有重复的纪录

题目解决方案

方法一:awk+substr()+sort+uniq

awk命令默认是以空格做为分隔符分割每一列的,在nowcoder.txt文件中,第四列为:

lucky@LAPTOP-G2DIO3FV:~$ cat nowcoder.txt | awk '{print $4}'
[21/Apr/2020:14:12:49
[21/Apr/2020:15:00:49
[21/Apr/2020:21:21:49
[21/Apr/2020:22:10:49
[22/Apr/2020:15:00:49
[22/Apr/2020:15:26:49
[23/Apr/2020:08:05:49
[23/Apr/2020:09:20:49
[23/Apr/2020:10:27:49
[23/Apr/2020:10:27:49
[23/Apr/2020:14:12:49
[23/Apr/2020:15:00:49
[23/Apr/2020:15:00:49
[23/Apr/2020:16:15:49
[23/Apr/2020:20:27:49
[23/Apr/2020:20:27:49
[23/Apr/2020:20:27:49
[23/Apr/2020:20:27:49
[23/Apr/2020:20:27:49
[23/Apr/2020:22:10:49
[23/Apr/2020:23:59:49

题目要求统计每分钟的请求数,那么只需提出小时和分钟就行了。awk命令的substr()函数可以提取出子串:

lucky@LAPTOP-G2DIO3FV:~$ cat nowcoder.txt | awk '{print substr($4,14,5)}'
14:12
15:00
21:21
22:10
15:00
15:26
08:05
09:20
10:27
10:27
14:12
15:00
15:00
16:15
20:27
20:27
20:27
20:27
20:27
22:10
23:59

然后就很简单了,排序–>去重–>排序

lucky@LAPTOP-G2DIO3FV:~$ cat nowcoder.txt | awk '{print substr($4,14,5)}' | sort | uniq -c | sort -rn -k 1
      5 20:27
      4 15:00
      2 22:10
      2 14:12
      2 10:27
      1 23:59
      1 21:21
      1 16:15
      1 15:26
      1 09:20
      1 08:05

然后用awk把这两列提出取来就行了(去掉空格)。所以最终的解决方案的代码如下:

cat nowcoder.txt | awk '{print substr($4,14,5)}' | sort | uniq -c | sort -rn -k 1 | awk '{print $1,$2}'

方法二:awk+sort

在前面,我们提到awk命令默认以空格作为分隔符分割出每一列,现在关注nowcoder.txt文件的内容,我们发现只有在时间那里才有:,那么我们直接使用:作为分隔符,这样第二列和第三列就是我们想要的小时和分钟了。事实上,这个方法更简单。

lucky@LAPTOP-G2DIO3FV:~$ awk -F ":" '{print $2,$3}' nowcoder.txt
14 12
15 00
21 21
22 10
15 00
15 26
08 05
09 20
10 27
10 27
14 12
15 00
15 00
16 15
20 27
20 27
20 27
20 27
20 27
22 10
23 59

接下来,在awk中,用一个数组来进行时间上的计数:

lucky@LAPTOP-G2DIO3FV:~$ awk -F ":" '{a[($2":"$3)]++}END{for (i in a) {print a[i], i}}' nowcoder.txt
4 15:00
2 22:10
1 21:21
2 14:12
1 23:59
5 20:27
1 16:15
2 10:27
1 09:20
1 15:26
1 08:05

最后,排序就行了。最终的代码如下:

awk -F ":" '{a[($2":"$3)]++}END{for (i in a) {print a[i], i}}' nowcoder.txt | sort -h -r

方法三:awk+fprintf+sort

:分割,使用sprintf()提取相关内容

awk -F ":" '{
    a[sprintf("%s:%s", $2, $3)]++
} END {
    for (i in a) {
        printf("%d %s\n", a[i], i)
    }
}' | sort -r

方法四:awk+substr()+数组+sort

awk '{
    a[substr($4,14,5)]++ 
}END{
    for(i in a) {
        print a[i]" "i
    }
}' nowcoder.txt | sort -nrk1
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Shell牛客刷题系列】SHELL28 nginx日志分析6-统计每分钟的请求数 的相关文章

随机推荐

  • 批处理获取管理员权限

    废话少说 先上代码 echo off BatchGotAdmin REM gt Check for permissions IF PROCESSOR ARCHITECTURE EQU amd64 gt nul 2 gt 1 SYSTEMRO
  • TCP/IP体系结构简介

    一 网络体系的构成 访问方式 数据帧格式 布线类型 布线规则 二 网络体系的类型 IEEE 802 3 以太网 在大多数办公室和家庭中使用的基于线缆的网络 就是常见的有线局域网 IEEE 802 11 无线网络 在办公室 家庭和咖啡厅使用的
  • 如何下载和安装 Visual C++6.0(解决未响应版)

    下载链接一 https pan baidu com s 1VEggDaoKgt0ZW8Q5wSu zQ 提取码 4chy 下载链接二 https dl pconline com cn download 413670 html
  • 行列式&矩阵_MD&Latex

    行列式 left begin array cccc 1 6 9 7 90 f x 9 psi x g x end array right left begin array cccc 1 6 9 7 90 f x 9 psi x g x en
  • 使用mklink突破百度网盘等软件的自动备份文件夹数量限制

    百度网盘 夸克等各种网盘都提供了自动备份文件夹的功能 但一般都有文件夹数量的限制 比如百度网盘就限制了最多只能同时备份5个文件夹 想整盘备份的话显然是不够的 当然 你可以把所有的文件夹都转移到一个母文件夹下 但这样明显不太方便 操作起来还涉
  • 解决idea中maven的javaweb项目,输出在控制台上的中文乱码问题

    在idea中创建一个maven的javaweb项目 当有中文输出到控制台的时候 就会出现乱码 下图 第一张图是我们要输出的中文 但是我们通过servlet访问之后 控制台打印出来的都是乱码 而且我们使用的是maven自带的tomcat 所以
  • 6.6 Hessenberg法求特征值

    文章目录 1 Gram Schmidt正交化的缺点 2 Hessenberg矩阵 3 海森堡化简 Hessenberg reduction 4 Givens rotation 5 多次Givens rotation QR 6 循环QR直至收
  • Java并发编程学习11-任务执行演示

    Java并发编程学习系列 任务执行演示 引言 1 串行的页面渲染器 2 携带结果的任务 3 使用 Future 实现页面渲染器 4 使用 CompletionService 实现页面渲染器 5 为任务设置时限 5 1 限时获取广告信息示例
  • Qt线程基础使用指南

    Qt的线程一共3种使用方式 继承QThread 继承QRunnable 调用moveToThread 方法 本文旨在系统的记录这3种方法的使用过程 以及解决使用这些方法中遇到的bug 一 继承QThread 1 创建线程文件 继承基类QTh
  • day02 - Java基础语法

    day02 Java基础语法 0 类型转换问题 类型转换 理解 在Java中 会存在不同类型的数据需要一起参与运算 所以这些数据类型之间是需要相互转换的 分为两种情况 自动类型转换和强制类型转换 自动类型转换 类型范围小的变量 可以直接赋值
  • 手机探测帧频率的测试

    手机的探测帧的频率在802 11协议里面并没有一个详细的要求 并且各个厂家从省电等方面考虑设置的探测帧频率也各不相同 并且在wifi界面下 锁屏状态下 忽略掉wifi再锁屏的状态下探测帧的频率都不同 所以wifi探针并不是一个可靠的用户感知
  • FPGA实战小项目

    1 基于fpga俄罗斯方块的实现 基于fpga俄罗斯方块的实现 2 基于fpga白平衡的实现 基于fpga白平衡的实现 3 基于fpga的目标跟踪 树叶 基于fpga的目标跟踪 树叶 4 基于fpga数字0 9识别的实现 基于fpga数字0
  • HCNP——水平分割、毒性逆转、触发更新、毒性路由

    一 水平分割 原理是 RIP路由器从某个接口收到的路由不会再从该接口通告回去 这个机制很大程度上消除了RIP路由的环路隐患 二 毒性逆转 毒性逆转是另一种防止路由环路的有效机制 其原理是 RIP从某个接口学到路由后 当他从该接口发送Resp
  • linux的mtime的用法,Find–atime –ctime –mtime的用法与区别总结

    周五有同事问起find命令中 mtime n mtime n以及 mtime n的用法区别 当时虽然记得这里n是n个24个小时的意思 也是对所有这几个属性详细的用法却一知半解 索性周末仔细google并且实践了一番 终于理清楚了个中乾坤 f
  • SpringBoot分布式任务调度,可支持rabbitmq与kafka两种消息中间件的可回滚微服务实现。

    分布式任务调度管理 Distribution task center 支持Rabbit与kafka两种消息队列 实现立即执行与根据CronExpress表达式的执行及更加复杂的复合执行策略 在任务执行过程中可完成回滚操作 在微服务中我们经常
  • MyBatis-动态SQL

    实体类Car package com bjpowernode domain public class Car private Integer id private String carNum private String brand pri
  • qt 一个线程接收数据 主线程更新界面 会造成界面退出 怎么解决_打造一个好产品

    原标题 打造一个好产品 让产品自己说话 编辑导语 一个好的产品 关键在于产品经理和团队 产品经理对于产品如何理解以及产品更新迭代时的需求变化 产品如何实现更好的体验等等 本文作者分享了关于产品经理经常犯的七个问题 我们一起来看一下 不管怎么
  • c++双链表【构造函数、运算符重载、析构函数、增删查改及逆置等】

    c 中的双向链表写法 主要实现 增删查改 链表逆置 构造函数 运算符重载 等 建立头文件SList h pragma once typedef int DataType class ListNode friend class List 友元
  • Vue学习-基础篇4

    目录 组件结构讲解 如何在组件中引入其它组件 组件中如何使用外部插件 组件间的传值 Vue cli项目创建 什么是脚手架 创建项目 Vue cli项目结构 Vue cli 入口文件main js分析 组件结构讲解 把每个组件都放到一个独立的
  • 【Shell牛客刷题系列】SHELL28 nginx日志分析6-统计每分钟的请求数

    该系列是基于牛客Shell题库 针对具体题目进行查漏补缺 学习相应的命令 刷题链接 牛客题霸 Shell篇 该系列文章都放到专栏下 专栏链接为 专栏 Shell 欢迎关注专栏 本文知识预告 本文首先学习了sprintf 函数的用法 然后复习