用sed处理Apache日志文件
统计Apache日志单IP访问请求数排名。假设Apache日志内容access.log内容为:
10.0.0.41 - - [03/Dec/2010:23:27:01 +0800] "HEAD /checkstatus.jsp HTTP/1.0" 200 - 10.0.0.43 - - [03/Dec/2010:23:27:01 +0800] "HEAD /checkstatus.jsp HTTP/1.0" 200 - 10.0.0.42 - - [03/Dec/2010:23:27:01 +0800] "HEAD /checkstatus.jsp HTTP/1.0" 200 - 10.0.0.46 - - [03/Dec/2010:23:27:02 +0800] "HEAD /checkstatus.jsp HTTP/1.0" 200 - 10.0.0.42 - - [03/Dec/2010:23:27:02 +0800] "HEAD /checkstatus.jsp HTTP/1.0" 200 - 10.0.0.47 - - [03/Dec/2010:23:27:02 +0800] "HEAD /checkstatus.jsp HTTP/1.0" 200 - 10.0.0.41 - - [03/Dec/2010:23:27:02 +0800] "HEAD /checkstatus.jsp HTTP/1.0" 200 - 10.0.0.47 - - [03/Dec/2010:23:27:02 +0800] "HEAD /checkstatus.jsp HTTP/1.0" 200 - 10.0.0.41 - - [03/Dec/2010:23:27:03 +0800] "HEAD /checkstatus.jsp HTTP/1.0" 200 - 10.0.0.46 - - [03/Dec/2010:23:27:03 +0800] "HEAD /checkstatus.jsp HTTP/1.0" 200 -
下面给出3种解决方案:
方法1:
[root@home /]# awk '{++S[$1]} END{for (home in S) print home ,S[home]}' access.log |sort -rn -k2
10.0.0.41 3
10.0.0.47 2
10.0.0.46 2
10.0.0.42 2
10.0.0.43 1
#提示:$1为第一个域的内容。sort中的 -k2 为对第二个字段排序,即对数量排序。
方法2:
[root@home /]# awk '{print $1}' access.log|sort|uniq -c |sort -rn -k1
3 10.0.0.41
2 10.0.0.47
2 10.0.0.46
2 10.0.0.42
1 10.0.0.43
提示:这个方法是容易想到的简单易用的方法。
方法3:
[root@home /]# sed 's/ - -.*$//g' access.log|sort|uniq -c|sort -rn -k1
3 10.0.0.41
2 10.0.0.47
2 10.0.0.46
2 10.0.0.42
1 10.0.0.43
提示:sed管道后的第一个sort是让所有一样的IP挨着,因为uniq -c只能对相邻的IP行去重计数。
uniq -c 显示唯一的行,并在每行行首加上出现的次数
sort -k1 -nr 按照第一个字段,进行数值排序,且为逆序
head -10 取前10行数据
注意:默认情况下sed并不会对文件的内容进行修改,只是把处理过后的内容输出,如果你要写回文件,你可以使用重定向,如:
$ sed "s/my/yours/g" pets.txt > yours_pets.txt #不能重定向到原文件!
或使用 -i 参数直接修改文件内容:
$ sed -i "s/my/yours/g" pets.txt
[小乐趣]用命令在终端上打印出乘法口诀表:
-
# seq 1 | awk '{for(i=1;i<10;i++){for(j=1;j<10;j++){printf "%d%s%d%s%dt",j,"+",i,"=",i+j;}printf "n"}}'
-
# seq 1 | awk '{for(i=1;i<10;i++){for(j=1;j<=i;j++){printf "%d%s%d%s%dt",j,"+",i,"=",i+j;}printf "n"}}'
-
# awk 'BEGIN{for(i=9;i>0;i--){for(j=9;j>=i;j--){printf "%d%s%d%s%dt",j,"-",i,"=",j-i;}printf "n"}}'
#!/bin/bash for ((i=1;i<=9;i++)) do for ((j=1;j<=i;j++)) do echo -n " $j*$i=$(($i*$j)) " done echo done
sed基本命令笔记
替换字符(含有/特殊符号的),这里命令中的斜杠/可以用其他符号代替,如用#号(如把/用@替换)
$ echo "http://ixyzero.com/" | sed 's#/#@#g'
如果一定要用/斜杠的话,命令必须把要替换的斜杠先转义
$ echo "http://ixyzero.com/" | sed 's///@/g'
匹配内容前/后增加某内容
在匹配forbag.cn内容后增加myfreelinux.com这一行内容
sed ‘/forbag.cn/amyfreelinux.com’ youfile
在匹配forbag.cn内容前增加myfreelinux.com这一行内容
sed ‘/forbag.cn/imyfreelinux.com’ youfile
$ sed ‘s#10#100#g’ example—–不论什么字符,紧跟着s命令的都被认为是新的分隔符,所以,“#”在这里是分隔符,代替了默认的“/”分隔符。表示把所有10替换成100。
选定行的范围:逗号
$ sed -n '/test/,/check/p' example
所有在模板test和check所确定的范围内的行都被打印
$ sed -n '5,/^test/p' example
打印从第五行开始到第一个包含以test开始的行之间的所有行
$ sed '/test/,/check/s/$/sed test/' example
对于模板test和check之间的行,每行的末尾用字符串sed test替换
从文件读入:r命令
$ sed '/test/r file' example
file里的内容被读进来,显示在与test匹配的行后面,如果匹配多行,则file的内容将显示在所有匹配行的下面
[root@Jason64-17 sed]# cat sed-4.txt 1 2 3 4 5 6 7 8 9 10 [root@Jason64-17 sed]# cat sed-1.txt i am lisp i have a computer it is very big and i have no room google is very nice my friends usual alias nice for me [root@Jason64-17 sed]# sed '/very/r sed-4.txt' sed-1.txt i am lisp i have a computer it is very big 1 2 3 4 5 6 7 8 9 10 and i have no room google is very nice 1 2 3 4 5 6 7 8 9 10 my friends usual alias nice for me [root@Jason64-17 sed]# sed -n '/very/r sed-4.txt' sed-1.txt 1 2 3 4 5 6 7 8 9 10 1 2 3 4 5 6 7 8 9 10
#删除带有script关键字的行
# sed '/script/d'
# 删除文件中的最后一行
# sed '$d'
# 打印/复制文档中两个正则表达式之间的内容(相当有用啊!)
# sed '/Iowa/,/Montana/p'
# 在每一行开头处插入5个空格(使全文向右移动5个字符的位置)
# sed 's/^/ /'
# 对文件中的所有行编号(行号在左,文字右端对齐)
# sed = filename | sed 'N; s/^/ /; s/ *(.{6,})n/1 /'
# 对文件中的所有行编号,但只显示非空白行的行号
# sed '/./=' filename | sed '/./N; s/n/ /'
选择性地删除特定行:
# 显示通篇文档,除了两个正则表达式之间的内容 sed '/Iowa/,/Montana/d' # 删除文件中相邻的重复行(模拟 uniq 命令) # 只保留重复行中的第一行,其他行删除 sed '$!N; /^(.*)n1$/!P; D' # 删除文件中的重复行,不管有无相邻。注意hold space所能支持的缓存 # 大小,或者使用GNU sed。 sed -n 'G; s/n/&&/; /^([ -~]*n).*n1/d; s/n//; h; P' # 删除除重复行外的所有行(模拟“uniq -d”) sed '$!N; s/^(.*)n1$/1/; t; D' # 删除文件中开头的10行 sed '1,10d' # 删除文件中的最后一行 sed '$d' # 删除文件中的最后两行 sed 'N;$!P;$!D;$d' # 删除文件中的最后10行 sed -e :a -e '$d;N;2,10ba' -e 'P;D' # 方法1 sed -n -e :a -e '1,10!{P;N;D;};N;ba' # 方法2 # 删除8的倍数行 gsed '0~8d' # 只对GNU sed有效 sed 'n;n;n;n;n;n;n;d;' # 其他sed # 删除匹配式样的行 sed '/pattern/d' # 删除含pattern的行。当然pattern # 可以换成任何有效的正则表达式 # 删除文件中的所有空行(与“grep '.' ”效果相同) sed '/^$/d' # 方法1 sed '/./!d' # 方法2 # 只保留多个相邻空行的第一行。并且删除文件顶部和尾部的空行。 # (模拟“cat -s”) sed '/./,/^$/!d' #方法1,删除文件顶部的空行,允许尾部保留一空行 sed '/^$/N;/n$/D' #方法2,允许顶部保留一空行,尾部不留空行 # 只保留多个相邻空行的前两行。 sed '/^$/N;/n$/N;//D' # 删除文件顶部的所有空行 sed '/./,$!d' # 删除文件尾部的所有空行 sed -e :a -e '/^n*$/{$d;N;ba' -e '}' # 对所有sed有效 sed -e :a -e '/^n*$/N;/n$/ba' # 同上,但只对 gsed 3.02.*有效 # 删除每个段落的最后一行 sed -n '/^$/{p;h;};/./{x;/./p;}'
编号: -------- # 为文件中的每一行进行编号(简单的左对齐方式)。这里使用了“制表符” # (tab,见本文末尾关于't'的用法的描述)而不是空格来对齐边缘。 sed = filename | sed 'N;s/n/t/' # 对文件中的所有行编号(行号在左,文字右端对齐)。 sed = filename | sed 'N; s/^/ /; s/ *(.{6,})n/1 /' # 对文件中的所有行编号,但只显示非空白行的行号。 sed '/./=' filename | sed '/./N; s/n/ /'
# 只在行中出现字串“baz”的情况下将“foo”替换成“bar” sed '/baz/s/foo/bar/g' # 将“foo”替换成“bar”,并且只在行中未出现字串“baz”的情况下替换 sed '/baz/!s/foo/bar/g' # 不管是“scarlet”“ruby”还是“puce”,一律换成“red” sed 's/scarlet/red/g;s/ruby/red/g;s/puce/red/g' #对多数的sed都有效 gsed 's/scarlet|ruby|puce/red/g' # 只对GNU sed有效
# 只显示匹配正则表达式的行(模拟“grep”) sed -n '/regexp/p' # 方法1 sed '/regexp/!d' # 方法2 # 只显示“不”匹配正则表达式的行(模拟“grep -v”) sed -n '/regexp/!p' # 方法1,与前面的命令相对应 sed '/regexp/d' # 方法2,类似的语法
sed的速度优化:当由于某种原因(比如输入文件较大、处理器或硬盘较慢等)需要提高 命令执行速度时,可以考虑在替换命令(“s/.../.../”)前面加上地址表达式来 提高速度。举例来说: sed 's/foo/bar/g' filename # 标准替换命令 sed '/foo/ s/foo/bar/g' filename # 速度更快 sed '/foo/ s//bar/g' filename # 简写形式 当只需要显示文件的前面的部分或需要删除后面的内容时,可以在脚本中使用“q” 命令(退出命令)。在处理大的文件时,这会节省大量时间。因此: sed -n '45,50p' filename # 显示第45到50行 sed -n '51q;45,50p' filename # 一样,但快得多
以上的大部分内容是从sed1line中文版中摘抄和学习然后自己进行了测试的,于此做个记录。
《“sed用法学习[不定期更新]”》 有 1 条评论
Linux生产环境上,最常用的一套“Sed“技巧
https://mp.weixin.qq.com/s/wP9_wvoTARRrlszsOmvMgQ
https://github.com/aureliojargas/sokoban.sed