优秀的编程知识分享平台

网站首页 > 技术文章 正文

inux 文本处理三剑客--grep/sed/awk

nanyue 2024-09-10 16:12:38 技术文章 8 ℃

1. grep

实例1.统计出日志中的top10ip地址

 #ip正则表达式:\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}
 # -E 用于使用正则匹配,-o 只显示匹配的部分  -i忽略大小写,
grep -i -o -E '([0-9]{1,3}\.){3}[0-9]{1,3}' ihdmgr.log|sort -n|uniq -c|sort -n -r|head -10
    244 1.19.122.186
    243 1.19.57.41
    242 1.19.84.80
    242 1.19.104.57
    241 1.20.188.110
    241 1.19.50.149
    241 1.19.122.165
    240 1.19.84.66
    240 1.19.57.23
    239 1.19.121.248

2. sed

sed 命令的格式如下:sed options script file

  • 实例1:流式替换
# echo "This is a test" | sed 's/test/big test/'
This is a big test
  • 实例2:不修改源文件情况下的替换
[root@localhost shell]# cat data.txt 
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
[root@localhost shell]# 
[root@localhost shell]# 
# 源文件并没有发生变化
[root@localhost shell]# sed 's/dog/cat/' data.txt 
The quick brown fox jumps over the lazy cat.
The quick brown fox jumps over the lazy cat.
The quick brown fox jumps over the lazy cat.
The quick brown fox jumps over the lazy cat.
  • 实例3::在命令行使用多个编辑器命令。

要在 sed 命令行上执行多个命令时,只要用 -e 选项就可以了。命令-e可以省略。命令之间必须用分号隔开,并且在命令末尾和分号之间不能有空格。

sed -e 's/brown/green/; s/dog/cat/' data.txt 
The quick green fox jumps over the lazy cat.
The quick green fox jumps over the lazy cat.
The quick green fox jumps over the lazy cat.
The quick green fox jumps over the lazy cat.
  • 实例4:从文件中读取编辑器命令

如果有大量要处理的 sed 命令,那么将它们放进一个单独的文件中通常会更方便一些

[root@localhost shell]# cat wgw_script1.sed
s/brown/green/
s/fox/elephant/
s/dog/cat/
[root@localhost shell]# 
[root@localhost shell]# sed -f wgw_script1.sed data.txt
The quick green elephant jumps over the lazy cat.
The quick green elephant jumps over the lazy cat.
The quick green elephant jumps over the lazy cat.
The quick green elephant jumps over the lazy cat.
[root@localhost shell]#
  • 实例5:更多的替换选项

有4种可用的替换标记:

? 数字,表明新文本将替换第几处模式匹配的地方;

? g ,表明新文本将会替换所有匹配的文本;

? p ,表明原先行的内容要打印出来;

? w file ,将替换的结果写到文件中。

[root@localhost shell]# cat data1.txt 
This is a test of the test script.
This is the second test of the test script.
[root@localhost shell]# 
# 只能替换每行中第一个出现的字符。
[root@localhost shell]# sed 's/test/trial/' data1.txt
This is a trial of the test script.
This is the second trial of the test script.
[root@localhost shell]# 
# 替换第二个字符,第一个不变。
[root@localhost shell]# sed 's/test/trial/2' data1.txt 
This is a test of the trial script.
This is the second test of the trial script.
[root@localhost shell]# 
# 所有的都替换
[root@localhost shell]# sed 's/test/trial/g' data1.txt  
This is a trial of the trial script.
This is the second trial of the trial script.
[root@localhost shell]# 
# 只替换匹配行中第一个出现的字符,并打印出匹配的行
[root@localhost shell]# sed -n 's/test/trial/p' data1.txt 
This is a trial of the test script.
This is the second trial of the test script.
[root@localhost shell]# 
# 替换所有匹配行,并将替换后的结果存储到data1.txt.bak文件
sed 's/test/trial/g w  data1.txt.bak' data1.txt
cat data1.txt.bak
This is a trial of the trial script.
This is the second trial of the trial script.


sed 's/\/bin\/bash/\/bin\/csh/' /etc/passwd
# 感叹号被用作字符串分隔符,功能如上
sed 's!/bin/bash!/bin/csh!' /etc/passwd 
# 只修改第2行
sed '2s/dog/cat/' data1.txt
# 2,3行进行修改  
sed '2,3s/dog/cat/' data1.txt  
# 第2行开始的所有行  
sed '2,$s/dog/cat/' data1.txt 
# 对fox所在行的所有dog替换为cat。
sed '/fox/s/dog/cat/' data1.txt   
sed '2{s/fox/elephant/;s/dog/cat/}' data1.txt
# 从第二行开始修改两处。
sed '2,${s/fox/elephant/;s/dog/cat/}' data1.txt
  • 实例6. 删除行 并不会对源文件进行删除操作,只是输出结果。
# 删除所有行
sed 'd' data1.txt 
# 删除2,3行  
sed '2,3d' data1.txt 
# 删除第二行开始的所有行 
sed '2,$d' data1.txt 
# 删除含有dog的行 
sed '/dog/d' data1.txt  
# 你指定的第一个模式会“打开”行删除功能,第二个模式会“关闭”行删除功能 
sed '/1/,/3/d' date2.txt
  • 实例7. 插入或者附加文本插入( insert )命令( i )会在指定行前增加一个新行;附加( append )命令( a )会在指定行后增加一个新行。
# 插入一行
# echo "Test Line 2" | sed 'i\Test Line 1'
Test Line 1
Test Line 2
# 附加一行
# echo "Test Line 2" | sed 'a\Test Line 1'
Test Line 2
Test Line 1
# 第三行前插入一条数据
sed '3i\This is an inserted line.' data.txt 
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
This is an inserted line.
The quick brown fox jumps over the lazy cat.
The quick brown fox jumps over the lazy dog.
# 文章末尾添加一行数据
# sed '$a\This is a new line of text.' data.txt
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy cat.
The quick brown fox jumps over the lazy dog.
This is a new line of text.
# 在文章开头插入两行数据
[root@localhost shell]# sed '1i\This is one line of new text.\
This is another line of new text.' data.txt
This is one line of new text.
This is another line of new text.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy cat.
The quick brown fox jumps over the lazy dog.
[root@localhost shell]#

  • 实例8: 修改行
# 修改第三行
# sed '3c\ This is a changed line of text.' data.txt
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
 This is a changed line of text.
The quick brown fox jumps over the lazy dog.
# 文本寻址  对包含cat的行进行改写
sed '/cat/c\ "This is": "a changed line of text".' data.txt 
The quick brown fox jumps over the lazy dog.
The quick brown fox jumps over the lazy dog.
 "This is": "a changed line of text".
The quick brown fox jumps over the lazy dog.
# 把2,3行合并进行替换为一行。而不是逐一修改这两行文本
# sed '2,3c\ This is a new line of text.' data.txt
The quick brown fox jumps over the lazy dog.
 This is a new line of text.
The quick brown fox jumps over the lazy dog.
  • 实例9:转换命令

[address]y/inchars/outchars/

转换命令会对 inchars 和 outchars 值进行一对一的映射。inchars 中的第一个字符会被转

换为 outchars 中的第一个字符,第二个字符会被转换成 outchars 中的第二个字符。这个映射过程会一直持续到处理完指定字符。如果 inchars 和 outchars 的长度不同,则sed编辑器会产生一条错误消息。

[root@localhost shell]# cat data3.txt 
this is line 1
this is line 1
this is line 2
this is line 3
this is line 4
this is line 5
[root@localhost shell]# sed 'y/123/789/' data3.txt
this is line 7
this is line 7
this is line 8
this is line 9
this is line 4
this is line 5
# 报错 
sed 'y/1230/789/' data3.txt
sed: -e expression #1, char 11: strings for `y' command are different lengths
  • 实例10:写入文件
# 把data3.txt中的1-4行数据 写入test.txt文件。
[root@localhost shell]# sed '1,4w test.txt' data3.txt
this is line 1
this is line 1
this is line 2
this is line 3
this is line 4
this is line 5
[root@localhost shell]# 
[root@localhost shell]# 
[root@localhost shell]# cat test.txt 
this is line 1
this is line 1
this is line 2
this is line 3
  • 实例11:从文件中写入数据
[root@localhost shell]# sed '1r test.txt' data3.txt
this is line 1
this is line 1
this is line 1
this is line 2
this is line 3
this is line 1
this is line 2
this is line 3
this is line 4
this is line 5
[root@localhost shell]# cat test.txt 
this is line 1
this is line 1
this is line 2
this is line 3
[root@localhost shell]# cat data3.txt 
this is line 1
this is line 1
this is line 2
this is line 3
this is line 4
this is line 5
  • 实例12:删除空白行
[root@localhost shell]#  sed '/^$/d' data4.txt
This is the header line.
This is a data line.
This is the last line.
[root@localhost shell]# 
[root@localhost shell]# 
[root@localhost shell]# cat data4.txt 
This is the header line.


This is a data line.


This is the last line.
[root@localhost shell]#
  • 实例13:&符号的使用:
echo "The cat sleeps in his hat." | sed 's/.at/"&"/g'
The "cat" sleeps in his "hat".

实例14:剔除html标签

[hdm@SN_RMS_ACS4 tmp]$ sed 's/<[^>]*>//g' data5.txt




This is the page title






This is the first line in the Web page.
This should provide some useful
information to use in our sed script.




[hdm@SN_RMS_ACS4 tmp]$ 
[hdm@SN_RMS_ACS4 tmp]$ 
[hdm@SN_RMS_ACS4 tmp]$ cat data5.txt 
<html>
<head>
<title>This is the page title</title>
</head>
<body>
<p>
This is the <b>first</b> line in the Web page.
This should provide some <i>useful</i>
information to use in our sed script.
</body>
</html>
[hdm@SN_RMS_ACS4 tmp]$ 
# 删除多余的空白行。
[hdm@SN_RMS_ACS4 tmp]$ sed 's/<[^>]*>//g ; /^$/d' data5.txt   
This is the page title
This is the first line in the Web page.
This should provide some useful
information to use in our sed script.
[hdm@SN_RMS_ACS4 tmp]$

实例15:mongdb 数据导出格式规整

# 添加引号及逗号
sed -i 's/^/"&/g;s/$/&",/g' file
# 改造为json串
sed -i "\$s/,//g;1s/^/{sn:{\$in:[&/;\$s/$/&]}}/" file
# 泵出数据
opt/hdm/mongo/mongodb-linux-x86_64-rhel62-3.6.14/bin/mongoexport --authenticationDatabase=admin -uroot -p1qaz@WSX -d tms3 -c device --type csv -o download/sn_0316 -f sn,fac,model,sVer --queryFile=upload/sn_0316 
{mac:{$in:["F4:4C:70:77:5C:3E",
"F4:4C:70:77:5B:4D",
"F4:4C:70:77:5A:DA",
"F4:4C:70:77:5A:6D",
"AC:00:D0:3C:F8:18"]}}

3. awk

主要用作文本报告生成器来使用。

gawk程序的基本格式如下:

gawk options program file

常用选项参数:

-F:用于指定输入时用到的字段分隔符;

-v var=value:自定义变量;

实例1:点击回车出现hello world

gawk '{print "Hello World!"}'

实例2:使用数据字段变量

cat test1.txt 
One line of test text.
Two lines of test text.
Three lines of test text. 
# $0 代表整个文本行;$n 代表文本行中的第n个数据字段。超过列,不显示。
gawk '{print $1}' test1.txt 
One
Two
Three
# 按照冒号分割,显示系统里的账号。
gawk -F : '{print $1}' /etc/passwd

实例3:在程序脚本中使用多个命令,使用分号分割

# 赋值第四个字段,然后在打印全行。
echo "My name is Rich" | gawk '{$4="Christine"; print $0}'
My name is Christine
# 给第四个元素赋值为空,变相删除
echo "my name is rich"| gawk -F " " '{print $4=null;print $0}'
my name is 

实例4:从文件中读取程序

# 找出每个用户的家目录。
cat script1.gawk
{print $1 "'s home directory is " $6}
gawk -F: -f script1.gawk /etc/passwd|head -3
root's home directory is /root
bin's home directory is /bin
daemon's home directory is /sbin


# 定义了一个变量来保存 print 命令中用到的文本字符串。
# 注意,gawk程序在引用变量值时并未像shell脚本一样使用美元符
cat script2.gawk
{
text = "'s home directory is "
print $1 text $6
}
gawk -F: -f script2.gawk /etc/passwd|head -2
root's home directory is /root
bin's home directory is /bin

实例5:在处理数据前运行脚本:begin

通常用来打印报告标题,仅在开始处理文件中的文本之前执行一次;

gawk 'BEGIN {print "Hello World!"}'
Hello World!


gawk 'BEGIN {print "The test1 File Contents:"} {print $0}' test1.txt
The test1 File Contents:
One line of test text.
Two lines of test text.
Three lines of test text.


实例6:在处理数据后运行脚本:end

通常用来打印页脚,仅在文本处理完成之后、命令结束之前执行一次;

gawk 'BEGIN {print "The test1 File Contents:"}{print $0} END {print "End of File"}' test1.txt
The test1 File Contents:
One line of test text.
Two lines of test text.
Three lines of test text.
End of File


# 打印出每个用户的shell环境。
cat script3.gawk
BEGIN {
print "The latest list of users and shells"
print " UserID \t Shell"
print "-------- \t -------"
FS=":"
}
{
print $1 " \t " $7
}
END {
print "This concludes the listing"
}


gawk -f script3.gawk /etc/passwd
The latest list of users and shells
 UserID          Shell
--------         -------
root     /bin/bash
bin      /sbin/nologin
daemon   /sbin/nologin
....
This concludes the listing


实例7:使用变量

内建变量

1.字段和记录分割符变量

gawk数据字段和记录变量

变 量 描 述

FIELDWIDTHS 由空格分隔的一列数字,定义了每个数据字段确切宽度

FS 输入字段分隔符

RS 输入记录分隔符

OFS 输出字段分隔符

ORS 输出记录分隔符

ARGC 当前命令行参数个数

ARGIND 当前文件在 ARGV 中的位置

ARGV 包含命令行参数的数组

CONVFMT 数字的转换格式(参见 printf 语句),默认值为 %.6 g

ENVIRON 当前shell环境变量及其值组成的关联数组

ERRNO 当读取或关闭输入文件发生错误时的系统错误号

FILENAME 用作gawk输入数据的数据文件的文件名

FNR 当前数据文件中的数据行数

IGNORECASE 设成非零值时,忽略 gawk 命令中出现的字符串的字符大小写

NF 数据文件中的字段总数

NR 已处理的输入记录数

OFMT 数字的输出格式,默认值为 %.6 g

RLENGTH 由 match 函数所匹配的子字符串的长度

RSTART 由 match 函数所匹配的子字符串的起始位置


# 添加行号
awk 'BEGIN{FS=":"}{print NR,$1,$(NF-0)}' /etc/passwd |head -4
1 root /bin/bash
2 bin /sbin/nologin
3 daemon /sbin/nologin
4 adm /sbin/nologin
# 指定格式输出
awk 'BEGIN{FS=":"} OFS="\t"{print NR,$1,$NF}' /etc/passwd
1       root    /bin/bash
2       bin     /sbin/nologin
3       daemon  /sbin/nologin
4       adm     /sbin/nologin
# 固定宽度输出
gawk 'BEGIN{FIELDWIDTHS="3 5 2 5"}{print $1,$2,$3,$4}' test1.txt 
One  line  o f tes
Two  line s  of te
Thr ee li ne s of  
# 换行拼接
gawk 'BEGIN{FS="\n"; RS=""} {print $1,$4}' data8.txt 
# 命令行解析
gawk 'BEGIN{print ARGC,ARGV[1]}' test1.txt 
2 test1.txt

实例8:数值计算

gawk 'BEGIN{x=4; x= x * 2 + 3; print x}'  
11
# 按列求和以及平均值:
awk '{a+=$1;b+=$2;c+=$3}END{print a/NR,b/NR,c}' test3
7.75 39.5 63
# 列求最大值,最小值
awk 'BEGIN{a=0}{if ($1>a) a=$1 }END{print a}' test3   
awk 'BEGIN{a=11111}{if ($1<a) a=$1 }END{print a}' test3  
# 全文求和:
for i in `cat test3`;do echo $i ;done |awk '{a+=$1}END{print a}' 
475
awk '{a[$1]=a[$1]" "$2}END{for(i in a)print i,a[i]}' test3 |awk '{print $1":",$2";",$3}'

实例9:基于匹配模式过滤出所要的行,然后取出对应的字段。

gawk 'BEGIN{FS=" "} /One/{print $1}' test1.txt 
One
# 匹配操作符(~):表示第一个字段中以字母t为开头的字段。注意区分大小写。
gawk 'BEGIN{FS=" "} $1 ~ /^t/{print $0}' test1.txt 
ten lines of test.
# 第一个字段不是t打头的记录
gawk 'BEGIN{FS=" "} $1 !~ /^t/{print $0}' test1.txt 


cat test1.txt 
One line of test text.
Two lines of test text.
Three lines of test text.
ten lines of test.
# 查询root用户组的所有用户。
gawk -F: '$4 == 0{print $1}' /etc/passwd
root
sync
shutdown
halt
operator

实例10: 结构化命令:if,while,for语句。

# 找出新创建的用户名
gawk -F : '{if ($3 >= 500) print $1}' /etc/passwd
nfsnobody
wlaqzc2018
sysweihu

实例11:内建函数

字符串函数:

  • asort(s [,d])

将数组s按数据元素值排序。索引值会被替换成表示新的排序顺序的连续数字。另外,如果指定了d,则排序后的数组会存储在数组d中

  • asorti(s [,d])

将数组s按索引值排序。生成的数组会将索引值作为数据元素值,用连续数字索引来表明排序顺序。另外如果指定了d,排序后的数组会存储在数组d中

  • gensub(r, s, h [, t])

查找变量$0或目标字符串t(如果提供了的话)来匹配正则表达式r。如果h是一个以g或G开头的字符串,就用s替换掉匹配的文本。如果h是一个数字,它表示要替换第h处r匹配的地方

  • gsub(r, s [,t])

查找变量$0或目标字符串t(如果提供了的话)来匹配正则表达式r。如果找到了,就全部替换成字符串s

  • index(s, t)

返回字符串t在字符串s中的索引值,如果没找到的话返回 0

  • length([s])

返回字符串s的长度;如果没有指定的话,返回$0的长度

  • match(s, r [,a])

返回字符串s中正则表达式r出现位置的索引。如果指定了数组a,它会存储s中匹配正则表达式的那部分

  • split(s, a [,r])

将s用 FS 字符或正则表达式r(如果指定了的话)分开放到数组a中。返回字段的总数

  • sprintf(format,variables)

用提供的format和variables返回一个类似于printf输出的字符串

  • sub(r, s [,t])

在变量$0或目标字符串t中查找正则表达式r的匹配。如果找到了,就用字符串s替换掉第一处匹配

  • substr(s, i [,n])

返回s中从索引值i开始的n个字符组成的子字符串。如果未提供n,则返回s剩下的部分

  • tolower(s)

将s中的所有字符转换成小写

  • toupper(s)

将s中的所有字符转换成大写

  • mktime(datespec)

将一个按YYYY MM DD HH MM SS [DST]格式指定的日期转换成时间戳值

  • strftime(format[,timestamp])

将当前时间的时间戳或timestamp(如果提供了的话)转化格式化日期(采用shell函数date()的格式)

  • systime( )返回当前时间的时间戳

实例讲解

# 大小写及长度获取
gawk 'BEGIN{x = "testing"; print toupper(x); print length(x) }'
TESTING
7
# 函数简单操作。
gawk '{print toupper($1),tolower($1),length($2),systime(),substr($2,0,2),sub("T","t",$0),substr($5, 1,10),split($0,a) }' test1.txt
ONE one 4 1617337092 li 0 text. 5
TWO two 5 1617337092 li 1 text. 5
THREE three 5 1617337092 li 1 text. 5
# 将数字全部替换为空格||
 awk 'BEGIN{info="this is a test2012test!";gsub(/[0-9]+/,"||",info);print info}'
 this is a test||test!
# 字符串分割,存储到数组a中。
echo "12:34:56" | awk '{split($0,a,":");print a[1],a[2],a[3]}'
12 34 56
# 字符长度
echo "123" | awk '{print length}'
3
# 把abc用def进行替换
echo "abacdefabcdef"|awk '$0 ~ /abc/ {gsub("abc", "def", $0); print $1, $3}'
abacdefdefdef
# 寻找字符传中是否有test 串,如果有的话则ok,否则no found。
awk 'BEGIN{info="this is a test2010test!";print index(info,"test")?"ok":"no found";}' 
ok
# 匹配索引位置检索。
awk 'BEGIN{info="this is a test2010test!";print match(info,/[0-9]+/)}' 
15
# 日期转换
 gawk 'BEGIN{
date = systime()
day = strftime("%A, %B %d, %Y", date)
print day
}'
星期五, 四月 02, 2021

实例12:字符串实战

awk -F "|" '/start to run in BootState/{print $1, substr($5,match($5,"deviceMac"),26),   substr($5,match($5,"deviceModel"),23)}' apmgr.log|head -2
2021-04-02 16:52:49,057  deviceMac":"A0D83D8A3380", deviceModel":"SR1041D",
2021-04-02 16:52:49,062  deviceMac":"24E4C8FA30A0", deviceModel":"SR1041D",

Tags:

最近发表
标签列表