[9] 正则表达式
正则表达式,又称规则表达式。(英语:Regular Expression,正则就是一串有规律的字符串。正则表通常被用来检索、替换那些符合某个模式(规则)的文本。
9.1 grep 命令详解
- 基本语法和参数
语法: grep [-cinvABC] '过滤关键词' 文件名
-c :打印符合要求的行数 只显示符合关键词的总行数,不显示行的内容
-n :在输出符合要求的行的同时连同行号一起输出
-i :不区分搜索大小写
-v :打印不符合要求的行
-r : 会把目录下面所有的文件全部遍历,不加r,只能针对文件搜索,而不是整个目录下的文件内容。遍历文件,同时也会把匹配到的关键字所在的文件路径列出来,如果不想列出文件路径,使用-h参数就可以了
-A :后跟一个数字(有无空格都可以),例如 –A2则表示打印符合要求的行以及下面两行
-B :后跟一个数字,例如 –B2 则表示打印符合要求的行以及上面两行
-C :后跟一个数字,例如 –C2 则表示打印符合要求的行以及上下各两行
--color: 把匹配到的关键词用红色标识 永久显示,把设置别名的命令写到.bashrc文件中,source .bashrc生效环境变量
. 表示任意一个字符;
* 表示零个或多个(任意个)前面的字符;
.* 表示零个或多个任意字符,空行也包含在内
- grep/egrep示例
过滤出带有某个关键词的行并输出行号
[root@localhost ~]# grep -n 'root' 1.txt
过滤出不带有某个关键词的行并输出行号
[root@localhost ~]# grep -n -v 'root' 1.txt
过滤出所有包含数字的行
[root@localhost ~]# grep '[0-9]' 1.txt
过滤出所有不包含数字的行
[root@localhost ~]# grep -v '[0-9]' 1.txt
过滤出包含root的行以及下面一行
[root@localhost ~]# grep -A1 'root' 1.txt
过滤出包含root的行以及上面一行
[root@localhost ~]# grep -B1 'root' 1.txt
过滤出包含root的行以及上下各一行
[root@localhost ~]# grep -C1 'root' 1.txt
去除所有以’#’开头的行
[root@localhost ~]# grep -v '^#' 1.txt
[root@localhost ~]# grep '^[#]' 1.txt
去除所有空行和以#开头的行
[root@localhost ~]# grep -v '^$' 1.txt|grep -v '^#'
过滤出以英文字母开头的行
[root@localhost ~]# grep '^[a-zA-Z]' 1.txt
过滤出以非数字开头的行
[root@localhost ~]# grep '^[^0-9]' 1.txt
说明: 在[]里面加^表示取非
grep '^[^0-9]' 1.txt 过滤出非数字开头的行,不包括数字、空行,包括特殊字符,字母,空格
grep -v '^[0-9]' 1.txt 过滤出非数字开头的行,不包括数字,包括空行、特殊字符、字母、空格
过滤任意一个或多个字符
[root@localhost ~]# grep 'r.o' 1.txt
[root@localhost ~]# grep 'r*t' 1.txt
[root@localhost ~]# grep 'r.*t' 1.txt
指定过滤字符次数
[root@localhost ~]# grep 'o\{2\}' 1.txt
grep 'o\{2\}' 1.txt == grep -E '(oo)' 1.txt
把一个目录下,过滤所有*.php文档中含有eval的行
[root@localhost ~]# grep -r --include="*.php" 'eval' /data/
- egrep 命令详解
egrep执行效果与”grep-E”相似,使用的语法及参数可参照grep指令,与grep的不同点在于解读字符串的方法。
指定过滤字符次数
[root@localhost ~]# egrep 'o{2}' 1.txt
匹配1个或1个以上+前面的字符
[root@localhost ~]# egrep 'o+' 1.txt
匹配0个或者1个?前面字符
[root@localhost ~]# egrep 'oo?' 1.txt
匹配roo或者nologin
[root@localhost ~]# egrep 'roo|nologin' 1.txt
匹配1个或者多个 ‘oo’
[root@localhost ~]# egrep '(oo)+' 1.txt
[root@localhost ~]# egrep '(oo){2}' 1.txt
用括号表示一个整体,下面例子会匹配roo或者ato
[root@localhost ~]# egrep 'r(oo)|(at)o' 1.txt
9.2 sed 命令详解
sed 可以过滤、替换、删除、调换
-r 对特殊字符进行脱意,等同于使用\
-n 只打印符合规范的行
/关键字/p 搜索打印关键字行
/关键字/d 搜索删除关键字行
s/关键字/替换内容/g 文本替换
- sed 示例
打印指定行
[root@localhost ~]# sed '10'p -n 1.txt
[root@localhost ~]# sed '1,4'p -n 1.txt
[root@localhost ~]# sed '5,$'p -n 1.txt
说明: 这里的p是print的意思,加上-n后就可以只打印符合规则的行,如果不加则会把1.txt从头到尾打印一遍。
打印包含某个字符串的行
[root@localhost ~]# sed -n '/root/'p 1.txt
可以使用 ^ . * $等特殊符号
[root@localhost ~]# sed -n '/ro.t/'p 1.txt
[root@localhost ~]# sed -n '/^roo/'p 1.txt
sed跟grep一样,不识别+|{}()等符号,需要借助脱义符号\或者使用选项-r
[root@localhost ~]# sed -nr '/ro+/'p 1.txt
[root@localhost ~]# sed -n '/ro\+/'p 1.txt 上面两个命令效果是一样的。
-e 可以实现同时进行多个任务
[root@localhost ~]# sed -e '/root/p' -e '/body/p' -n 1.txt
也可以用;实现
[root@localhost ~]# sed '/root/p; /body/p' -n 1.txt
删除指定行
[root@localhost ~]# sed '/root/d' 1.txt
sed '1d' 1.txt 删除第一行
sed '1,10d' 1.txt 删除1到10行
说明:'/root/d' 删除包含root的行
删除所有数字
[root@localhost ~]# sed 's/[0-9]//g' 1.txt
说明: 其实就是把所有数字替换为空字符,g的作用是替换一行中出现的所有字符
文本替换
[root@localhost ~]# sed '1,12s/root/toor/g' 1.txt
[root@localhost ~]# sed -r '1,12s/ro+/toor/g' 1.txt
删除所有非数字
[root@localhost ~]# sed 's/[^0-9]//g' 1.txt
调换两个字符串位置
[root@localhost ~]# head -n2 1.txt |sed -r 's/(root)(.*)(bash)/\3\2\1/'
说明:在sed中可以用()去表示一个整体,本例中把root和bash调换位置,后面的\1\2\3分别表示第一个小括号里面的,第二个小括号里面的以及第三个小括号里面的内容。
-i 选项可以直接修改文件内容
[root@localhost ~]# sed -i 's/ot/to/g' 1.txt
I参数匹配关键词时不区分大小写
[root@localhost ~]# sed -n '/root/'Ip 1.txt
grep使用-i选项不区分大小写
9.3 awk 命令详解
可以截取、过滤、数学计算
- awk示例
截取文档中的某段
[root@localhost ~]# awk -F ':' '{print $1}' 1.txt ==cut -d: -f1 1.txt
说明: -F指定分隔符号为:,若要截取所有的段,使用$0
也可以使用自定义字符连接每个段
[root@localhost ~]# awk -F':' '{print $1"#"$2"#"$3"#"$4}' 1.txt
或者使用awk内部变量OFS,格式如下:
# awk -F ':' '{OFS="#"} {print $1,$2,$3,$4}' 1.txt
匹配字符或字符串
[root@localhost ~]# awk '/oo/' 1.txt
针对某个段匹配
[root@localhost ~]# awk -F ':' '$1 ~/oo/' 1.txt
多次匹配
[root@localhost ~]# awk -F ':' '/root/ {print $1,$3}; $1 ~/test/; $3 ~/20/' 1.txt
条件操作符[==]、[>]、[<]、[!=]、[>=]、[<=]
第三段为0
[root@localhost ~]# awk -F ':' '$3=="0"' 1.txt
第三段大于等于500
[root@localhost ~]# awk -F ':' '$3>=500' 1.txt
说明:当比较数字时,不能加双引号,如果写成$3>="500"就不符合我们的需求了。
第七段不是'/sbin/nologin'
[root@localhost ~]# awk -F ':' '$7!="/sbin/nologin"' 1.txt
第三段小于第四段
[root@localhost ~]# awk -F ':' '$3<$4' 1.txt ;
第三段大于5,并且第三段小于7
[root@localhost ~]# awk -F ':' '$3>5 && $3<7' 1.txt
第三段大于5或者第七段为'/bin/bash'
[root@localhost ~]# awk -F ':' '$3>"5" || $7=="/bin/bash"' 1.txt
awk内置变量 NF(段数fields) NR(行数records)
[root@localhost ~]# head -n3 1.txt | awk -F ':' '{print NF}'
[root@localhost ~]# head -n3 1.txt | awk -F ':' '{print $NF}' 打印最后一段
[root@localhost ~]# head -n3 1.txt | awk -F ':' '{print NR}'
awk -F ':' '{print NF "#" NR}' passwd
打印20行以后的行
[root@localhost ~]# awk 'NR>20' 1.txt
打印20行以后并且第一段包含'ssh'的行
[root@localhost ~]# awk -F ':' 'NR>20 && $1 ~ /ssh/' 1.txt
更改某个段的值
[root@localhost ~]# awk -F ':' '$1="root"' 1.txt
数学计算, 把第三段和第四段值相加,并赋予第七段
[root@localhost ~]# awk -F ':' '{$7=$3+$4; print $0}' 1.txt
但是这样的话,相当于改变了原来文本的结构,所以print $0的时候就不再有分隔符显示。如果想显示分隔符需要借助OFS
[root@localhost ~]# awk -F ':' '{OFS=":"} {$7=$3+$4; print $0}' 1.txt
计算第三段的总和
[root@localhost ~]# awk -F ':' '{(tot=tot+$3)}; END {print tot}' 1.txt
awk中也可以使用if关键词
[root@localhost ~]# awk -F ':' '{if ($1=="root") print $0}' 1.txt
[root@localhost ~]# awk -F ':' '{if ($1~/root/) print $0}' $0代表整行
扩展
[root@localhost ~]# sed -n '/ens33/,/TX/p' 1.txt
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 192.168.1.11 netmask 255.255.255.0 broadcast 192.168.1.255
inet6 fe80::e573:6122:85e7:b1a9 prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:a0:8e:5c txqueuelen 1000 (Ethernet)
RX packets 716 bytes 68017 (66.4 KiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 570 bytes 65787 (64.2 KiB)
sed中,使用\u表示大写,\l表示小写
1. 把每个单词的第一个小写字母变大写:
sed 's/\b[a-z]/\u&/g' filename
2. 把所有小写变大写:
sed 's/[a-z]/\u&/g' filename
3. 大写变小写:
sed 's/[A-Z]/\l&/g' filename
[root@localhost ~]# sed -n "s/\(^a.*\)/\1 123/p" passwd
adm:x:3:4:adm:/var/adm:/sbin/nologin 123
abrt:x:173:173::/etc/abrt:/sbin/nologin 123
[root@test200 ~]# cat test
a
b
c
d
e
f
[root@test200 ~]# sed '/c/{p;:a;N;$!ba;d}' test
a
b
c
定义一个标签a,匹配c,然后N把下一行加到模式空间里,匹配最后一行时,才退出标签循环,然后命令d,把这个模式空间里的内容全部清除。
if 匹配"c"
:a
追加下一行
if 不匹配"$"
goto a
最后退出循环,d命令删除
[root@localhost ~]# sed -n '1,100{/abc/p}' 1.txt
A=44echo "ABCD" | awk -v GET_A=$A ’{print GET_A}’
说明:-v选项用于定义参数,这里表示将变量A的值赋予GET_A。
有多少个变量需要赋值,就需要多少个-v选项。与之等价的:应用于脚本中:
#! /bin/bash
sort -n filename |awk -F ':' '{print $1}'|uniq >id.txt
for id in `cat id.txt`; do
echo "[$id]"
awk -v id2=$id -F ':' '$1==id2 {print $2}' filename // 另外的方式为: awk -F ':' '$1=="'id'" {print $2}' filename
done
附件:
cat filename
1111111:13443253456
2222222:13211222122
1111111:13643543544
3333333:12341243123
2222222:12123123123
运行脚本后结果为:
[1111111]
13443253456
13643543544
[2222222]
13211222122
12123123123
[3333333]
12341243123
把两个文件中,第一列相同的行合并到同一行中
awk 'NR==FNR{a[$1]=$2}NR>FNR{print $0,a[$1]}' 1.txt 2.txt
解释:NR表示读取的行数,FNR表示读取的当前行数
所以其实NR==FNR 就表示读取2.txt的时候。 同理NR>FNR表示读取1.txt的时候
数组a其实就相当于一个map
a=`cat file`;echo $a
awk '{printf("%s ",$0)}' file // %s 后记得要有一空格,否则出来就是完全连在一起的,中间连空格都没有
cat file |xargs
passwd文件中把所有www替换为abc
awk 'gsub(/www/,"abc")' /etc/passwd
替换$1中的www为abc
awk -F ':' 'gsub(/www/,"abc",$1) {print $0}' /etc/passwd
for j in `seq 0 20`; do
let x=100*$j
let y=$x+1
let z=$x+100
for i in `seq $y $z` ; do
awk -v a=$i '{printf $a " "}' example.txt >>/tmp/test.txt
echo " " >>/tmp/test.txt
done
done
找出文件(filename)中包含123或者包含abc的行
grep -E '123|abc' filename
用egrep同样可以实现
egrep '123|abc' filename
awk 的实现方式
awk '/123|abc/' filename
用awk编写生成以下结构文件的程序。( 最后列使用现在的时间,时间格式为YYYYMMDDHHMISS) 各列的值应如下所示,每增加一行便加1,共500万行。
1,1,0000000001,0000000001,0000000001,0000000001,0000000001,0000000001,2005100110101
2,2,0000000002,0000000002,0000000002,0000000002,0000000002,0000000002,2005100110101
#! /bin/bash
for i in `seq 1 5000000`; do
n=`echo "$i"|awk '{print length($0)}'`
export m=$[10-$n]
export o=`perl -e '$a='0'; $b=$a x $ENV{"m"}; print $b;'`
export j=$i
p=`perl -e '$c=$ENV{"o"}.$ENV{"j"}; print $c;'`
echo "$i,$i,$p,$p,$p,$p,$p,$p,`date +%Y%m%d%H%M%S`"
done
awk 实现方式
awk 'BEGIN{for(i=1;i<=10;i++)printf("%d,%d,%010d,%010d,%010d,%010d,%010d,%010d,%d\n",i,i,i,i,i,i,i,i,strftime("%Y%m%d%H%M"))}'
awk '{print "This is a '"'"'"$1} filename
解释一下:在awk中使用脱义字符\是起不到作用的,如果想打印特殊字符,只能使用'""' 这样的组合才可以。
这里自左至右为单引号 双引号 双引号 单引号其中两个单引号为一对,两个双引号为一对。想脱义$那就是'"$"' 脱义单引号那就是 '"'"'
paste filename1 filename2
-d 指定连接符
paste -d '+' a.txt b.txt