目录
相关命令
grep:文本过滤(模式:pattern)工具:文本查找,寻找含有特定文本的行
grep, egrep, fgrep
sed:stream editor,文本编辑工具(行)
awk:Linux上的实现gawk,文本报告生成器,使文本更好看
正则表达式
模式:由正则表达式字符及文本字符所编写的过滤条件
正则表达式REGEXP:由一类特殊字符及文本字符所编写的模式,其中有些字符不表示字符字面意义,而表示控制或通配的功能
分两类:
基本正则表达式:BRE grep
扩展正则表达式:ERE grep -E, egrep
正则表达式引擎:用来检查表达式是否能够匹配到那行文本信息
基本正则表达式元字符:
字符匹配:
.: 匹配任意单个字符
[ ]: 匹配指定范围内的任意单个字符
[^]:匹配指定范围外的任意单个字符 注意^必须得在至少一个[ ]内
[:digit:]、[:lower:]、[:upper:]、[:alpha:]、[:alnum:]、[:punct:](所有标点符号)、[:space:]
次数匹配:
用在要指定次数的字符后面,用于指定前面的字符要出现的次数
*:匹配前面的字符任意次;>=0
例如:grep "x*y"
abxy
xay *:y前面的x出现0次,也符合匹配条件
xxxxxxy 默认工作在贪婪模式
与glob中的*有区别
.*:任意长度的任意字符,此时与glob中单个*意义一致
\?:匹配其前面的字符0或1次,即前面的可有可无
\+:匹配其前面的字符至少1次;>0
\{m\}:匹配前面的字符m次,有转义字符\才不会因为{ }而展开命令行
\{m,n\}:匹配前面的字符至少m次,至多n次 \{0,n\}:匹配前面的字符至多n次
\{m,\}:匹配前面的字符至少m次
位置锚定:
^:行首锚定;用于模式的最左侧;e.g. ^root
$:行尾锚定;用于模式的最右侧
^PATTERN$:用于模式匹配整行
^$:空行 ^[[:space:]]*$ 假空行
\< 或 \b:词首锚定:用于单词模式的左侧
\> 或 \b:词尾锚定:用于单词模式的右侧
\<PATTERN\>:匹配整个单词
分组:
\( \):将一个或多个字符捆绑在一起,当作一个整体进行处理
\(xy\)*ab
如果不加()
Note:分组括号中的模式匹配到的内容会被正则表达式引擎记录于内部的变量中,这些变量的命名方式为:\1, \2, \3, ...(\n为在grep的命名方式,其他可能是$n)
\1:从左侧起,第一个左括号以及与之匹配右括号之间的模式所匹配到的字符
\(ab\+\(xy\)*\):
\1:ab\+\(xy\)*
\2:xy
后向引用:引用前面的分组括号中的模式所匹配的字符,(而非模式本身)
e.g.1 匹配以t结尾,且t前面有1-3个字母
e.g.2 匹配符合条件(t结尾,在t之前有1-3个字母开头)的单词
e.g.3 首尾t前{1,3}个字母不同
e.g.4 运用后向匹配,首尾t前{1,3}个字母相同
扩展的正则表达式元字符:
字符匹配:
.: 匹配任意单个字符
[ ]: 匹配指定范围内的任意单个字符
[^]:匹配指定范围外的任意单个字符
次数匹配:
扩展的正则表达式次数匹配不用反斜线 \ 转义
*:匹配前面的字符任意次;>=0
?:0或1次
+:1次或多次
{m}:匹配m次
{m,n}:至少m,至多n次
位置锚定:
^:行首锚定
$:行尾锚定
\<, \b:词首锚定
\>, \b:词尾锚定
分组:
()
后向引用:\1, \2, ...
或者:
a|b a或者b
e.g. C|cat表示的是”C或cat”,而不是”Cat或cat”
(C|c)at表示是”Cat或cat”
一、grep
grep:Global search REgular expression and Print out the line.
1.作用
文本搜索工具,根据用户指定的“模式”对目标文本逐行进行匹配检查,打印匹配到的行
2.用法及常用选项
grep [OPTIONS] PATTERN [FILE...] 模式两边要加引号
选项:
--color=auto:对匹配到的文本着色显示
-v:显示不能够被pattern匹配到的行
-i:忽略字符大小写
-o:仅显示匹配到的字符串
-q:静默模式,不输出任何信息,只需要判断结果是否存在时有用
-A #:after, 后#行
-B #:before, 前#行
-C #:context, 前后各#行
-E:使用ERE;
-F:使用快速正则表达式
-P:使用prel正则表达式
练习1
①显示/proc/meminfo文件中以大小写s开头的行;(要求:使用两种方式)
方法1:[root@Tux ~]# grep '\<[Ss].*' /proc/meminfo
改进:grep ^[Ss] /proc/meminfo
方法2:[root@Tux ~]# grep -i ^S /proc/meminfo
方法3:grep -e ^S -e ^s /proc/meninfo
②显示/etc/passwd文件中不以/bin/bash结尾的行
[root@Tux ~]# grep -v '/bin/bash$' /etc/passwd
[root@Tux ~]# grep -v '(/bin/bash)\>' /etc/passwd (此做法错误:\>用于单词词尾锚定)
③显示/etc/passwd文件中ID号最大的用户的用户名
分析:cut 取用户名-> sort -k3n排序->如何取出最大一行 tail -1
[root@Tux ~]# sort -t:-k3n /etc/passwd | cut -d:-f1 | tail -1
nfsnobody
[root@Tux ~]# sort -t:-k3nr /etc/passwd | cut -d:-f1 | head -1
nfsnobody
④如果用户root存在,显示其默认的shell程序
# id root &> /dev/null && grep "^root\>" /etc/passwd | cut -d:-f7
P.S. 也可以将root后的\>换成:
⑤找出/etc/passwd中的两位或三位数
错误做法:grep '[0-9]\{2,3\}' /etc/passwd 这样提取出来的可能不是纯数字
# grep " \< [0-9]\{2,3\} \> " /etc/passwd
⑥显示/etc/rc.d/rc.sysinit文件中,至少以一个空白字符开头的且后面存非空白字符的行
错误做法:grep '^[[:space:]]\+.*[^[:space:]].*' /etc/rc.d/rc.sysinit
非空白字符没有仅跟在空白字符后面,题目理解有偏差
# grep "^[[:space:]]\+[^[:space:]]" /rc.d/rc.sysinit
⑦找出"netstat -tan"命令的结果中以'LISTEN'后跟0个、1个或多个空白字符结尾的行
# netstat -tan | grep "LISTEN[[:space:]]*$"
⑧添加用户bash、testbash、basher以及nologin(其shell为/sbin/nologin);而后找出/etc/passwd文件中用户名同shell名的行
错误做法1:grep '\<\([[:alpha:]].*\):.*\1\>' /etc/passwd 模式成了一个单词
错误做法2:grep '^\([[:alpha:]]\+\).*\1$' /etc/passwd
P.S. ↑[:alpha:]应改成 [:alnum:]
useradd -s /sbin/nologin nologin
# grep " ^\([[:alnum:]]\+ \> \) .* \1$ " /etc/passwd
3.egrep
支持扩展的正则表达式,相当于grep -E,其他选项和grep一样
egrep = grep -E
egrep [OPTIONS] PATTERN [FILE...]
练习2
①显示当前系统root、centos或user1用户的默认shell和UID
egrep '^root|centos|user1\>' /etc/passwd |cut -d: -f1,7,3 --output-delimiter=' '
# grep -E '^(root|centos|user1)\>' /etc/passwd | cut -d:-f1,3,7
②找出/etc/rc.d/init.d/functions文件(CentOS6)中某单词后面跟一个小括号的行
# grep -E -o "^[_[:alpha:]]+\(\)" /etc/rc.d/init.d/functions
③使用echo输出一绝对路径,使用egrep取出其基名
命令:basename 去掉前导的目录部分后打印"名称"
basename /usr/bin/sort 输出"sort"
命令:dirname 输出已经去除了尾部的"/"字符部分的名称;如果名称中不包含"/",则显示"."(表示当前目录)。
dirname /usr/bin/sort 输出"/usr/bin"
dirname stdio.h 输出"."
# echo "/mnt/sdc" | grep -E -o "[^/]+/?$" | cut -d"/" -f1
进一步地:使用egrep取出路径的目录名,类似于dirname命令的结果
# echo "/etc/rc.d/init.d" | egrep -o '^/.*/' | egrep -o '^/.*([[:alpha:]]|[0-9])\>' || echo "."
④找出ifconfig命令结果中1-255之间的数值
# ifconfig | egrep '\<1?[[:digit:]]{,2}\>|\<25[0-5]\>|\<2[0-4][[:digit:]]\>'
⑤找出ifconfig命令结果中的IP地址(满足x.x.x.x即可)
# ifconfig | egrep -o '([[0-9]]{1,2}|1[0-9]{1,2}|25[0-5]|2[0-4][[0-9])\.([0-9]{1,2}|1[0-9]{1,2}|25[0-5]|2[0-4][0-9])\.([0-9]{1,2}|1[0-9]{1,2}|25[0-5]|2[0-4][0-9])\.([0-9]{1,2}|1[[0-9]{1,2}|25[0-5]|2[0-4][[0-9])'
4. fgrep
不支持正则表达式搜索,全按字符本身搜索
二、sed
sed:Stream EDitor, 行编辑器
用法:
sed [option]... 'script' inputfile...
script:
'地址命令'
1.常用选项
行为模式:
sed读取每个文件,一次读一行,将读取的行放到内存的模式空间;编辑命令修改模式空间中的内容;(未编辑结束的内容可以暂时放到保持空间,而保持空间当中的内容又可以重新被读取到模式空间,随后可继续编辑)当所有操作完成后,sed将模式空间的最后内容打印到标准输出
选项:
-n:不输出模式中的内容至屏幕;默认行为是将模式空间的内容输出到屏幕
-e:多点编辑 P.S. 多点编辑时,每个模式前都要加-e
-f /PATH/TO/SCRIPT_FILE:从指定文件中读取编辑脚本
-r:支持使用扩展正则表达式
-i:原处编辑
2.地址定界
①不给地址:对全文进行处理
②单地址
#:指定的行
/pattern/:被此处模式所能够匹配到的每一行
③地址范围
#,#
#,+#
/pat1/,/pat2/
#,/pat1/
④ ~:步进
1~2
2~2
3.编辑命令
d:删除
p:显示模式空间中的内容
a \text:在行后面追加文本;支持使用\n实现多行追加
i \text:在行前面插入文本;支持使用\n实现多行插入
c \text:替换行为单行或多行文本
w /path/to/somefile:保存模式空间匹配到的行至指定文件中 (另存为)
r /path/from/somefile:读取指定文件的文本流至模式空间中匹配到的行的行后
=:为模式空间中的行打印行号
!:取反条件;
s///:查找替换;支持使用其它分隔符,s@@@,s###
替换标记:
g:行内全局替换
p:显示替换成功的行
w /PATH/TO/SOMEFILE:将替换成功的结果保存至指定文件中
P.S. p命令和要地址定界一起使用,也就是打印被定界匹配到的行
练习
①删除/boot/grub/grub.conf文件中所有以空白开头的行行首的空白字符;
sed 's@^[[:space:]]\+@@' /boot/grub/grub.conf
②删除/etc/fstab文件中所有以#开头,后面至少跟一个空白字符的行的行首的#和空白字符
sed 's@^#[[:space:]]\+@@' /etc/fstab
③echo一个绝对路径给sed命令,取出其基名;取出其目录名
取出其基名
echo /etc/rc.d/init.d/testsrv | sed "s@/.*/@@"
取出其目录名
echo "/etc/sysconfig/" | sed 's@[^/]\+/\?$@@'
4.高级编辑命令
h:把模式空间中的内容覆盖至保持空间中
H:把模式空间中的内容追加至保持空间中
g:从保持空间取出数据覆盖至模式空间 get
G:从保持空间取出内容追加至模式空间 Get
x:把模式空间中的内容与保持空间中的内容进行互换
n:读取匹配到的行的下一行至模式空间
N:追加匹配到的行的下一行至模式空间
d:删除模式空间中的行
D:删除多行模式空间中的所有行
示例
sed -n 'n;p' FILE:显示偶数行
sed '1!G;h;$!d' FILE:逆向显示文件内容
解析:
①第一个命令1!G
若取出的行不是文本中的第一行就将保持空间中的内容追加至模式空间,是第一行则不做任何操作
②第二个命令h
将模式空间的内容覆盖至保持空间
③第三个命令$!d
若取出的行不是文本中的最后一行就将删除模式空间中的最后一行,是文本中的最后一行则不做任何操作
P.S. 事实上使用tac命令即可实现同样的功能
sed '$!N;$!D' FILE:取出文件后两行
sed '$!d' FILE:取出文件最后一行
sed 'G' FILE:
sed '/^$/d;G' FILE:
sed 'n;d' FILE:显示奇数行
sed -n '1!G;h;$p' FILE:逆向显示文件中的每一行
三、awk
awk:报告生成器,格式化文本输出
AWK:Aho, Weinberger, Kernighan --> New AWK, NAWK
GNU awk, gawk
gawk - pattern scanning and processing language
基本用法:gawk [options] 'program' FILE ...
program:PATTERN{ACTION STATEMENTS}
语句之间用分号分隔
选项:
-F:指明输入时用到的字段分隔符
-v var=value:自定义变量 P.S 每声明一个变量时都要加-v选项
程序模型:
awk将输入流看做一连串记录的集合,每条记录可细分为多个字段
1.print
print item1, item2, ...
要点:
① 逗号分隔符
② 输出的各item可以是字符串,也可以是数值;当前记录的字段、变量或awk的表达式
③ 如省略item,相当于print $0;
2.变量
①内建变量
FS:input field seperator,输入字段分割字符,默认为空白字符
OFS:output field seperator,输出字段分割字符,默认为空白字符
RS:input record seperator,输入记录分割字符,即输入时的换行符
ORS:output record seperator,输出时的换行符
NF:number of field,字段数量
{print NF}, {print $NF} 在awk中引用变量时不需要加符号$(除了字段变量)
NR:number of record, 行数
FNR:各文件分别计数;行数
FILENAME:当前文件名
ARGC:命令行参数的个数
ARGV:数组,保存的是命令行所给定的各参数
②自定义变量
a. -v var=value
P.S. 变量名区分字符大小写,引用变量时不需要加符号$(除了字段)
b. 在program中直接定义
3.printf
格式化输出:printf FORMAT, item1, item2, ...
① FORMAT格式符必须给出
② 不会自动换行,需要显式给出换行控制符,\n
③ FORMAT中需要分别为后面的每个item指定一个格式化符号
格式符:
%c:显示字符的ASCII码
%d, %i:显示十进制整数
%e, %E:科学计数法数值显示
%f:显示为浮点数
%g, %G:以科学计数法或浮点形式显示数值
%s:显示字符串
%u:无符号整数
%%:显示%自身
修饰符:
#[.#]:第一个数字控制显示的宽度;第二个#表示小数点后的精度
%3.1f
-:左对齐
+:显示数值的符号
4.操作符
算术操作符:
x+y, x-y, x*y, x/y, x^y, x%y
-x:转换为数值
+x:转换为数值
字符串操作符:没有符号的操作符,字符串连接
赋值操作符:
=, +=, -=, *=, /=, %=, ^=
++, --
比较操作符:
>, >=, <, <=, !=, ==
模式匹配符:
~:是否匹配
!~:是否不匹配
逻辑操作符:
&&
||
!
函数调用:
function_name(argu1, argu2, ...)
条件表达式:
selector?if-true-expression:if-false-expression
# awk -F: '{$3>=1000?usertype="Common User":usertype="Sysadmin or SysUser"; printf "%15s:%-s\n",$1,usertype}' /etc/passwd
5.PATTERN
① empty:空模式,匹配每一行
② /regular expression/:仅处理能够被此处的模式匹配到的行
③ relational expression:关系表达式;结果有“真”有“假”;结果为“真”才会被处理
真:结果为非0值,非空字符串
④ line ranges:行范围
startline,endline:/pat1/,/pat2/
注意: 不支持直接给出数字的格式
~]# awk -F:'(NR>=2&&NR<=10){print $1}' /etc/passwd
⑤ BEGIN/END模式
BEGIN{}:仅在开始处理文件中的文本之前执行一次
常见用法:
变量初始化、打印输出表头
END{}:仅在文本处理完成之后执行一次
常见用法:
打印所有行的分析结果
# awk -F: 'BEGIN{printf "%10s %10s\n--------------------------\n","username","UID"}{printf "%10s %10s\n",$1,$3}' /etc/passwd
6.常用的action
① Expressions
② Control statements:if, while等
③ Compound statements:组合语句
④ input statements
⑤ output statements
7.控制语句
条件式执行
if(condition) {statments}
if(condition) {statments} else {statements}
重复执行
while(conditon) {statments}
do {statements} while(condition)
for(expr1;expr2;expr3) {statements}
break
continue
delete array[index]
delete array
exit
{ statements }
①if-else
语法:if(condition) statement [else statement]
~]# awk -F:'{if($3>=1000) {printf "Common user:%s\n",$1} else {printf "root or Sysuser:%s\n",$1}}' /etc/passwd
~]# awk -F:'{if($NF=="/bin/bash") print $1}' /etc/passwd
~]# awk '{if(NF>5) print $0}' /etc/fstab
~]# df -h | awk -F[%] '/^\/dev/{print $1}' | awk '{if($NF>=20) print $1}'
使用场景:对awk取得的整行或某个字段做条件判断
②while循环
语法:while(condition) statement
条件“真”,进入循环;条件“假”,退出循环
使用场景:对一行内的多个字段逐一类似处理时使用;对数组中的各元素逐一处理时使用
~]# awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {print $i,length($i); i++}}' /etc/grub2.cfg
匹配/etc/grub2.cfg以若干空格开头后接linux16的行,打印其中每个字段及其字符个数
~]# awk '/^[[:space:]]*linux16/{i=1;while(i<=NF) {if(length($i)>=7) {print $i,length($i)}; i++}}' /etc/grub2.cfg
③do-while循环
语法:do statement while(condition)
意义:至少执行一次循环体
④for循环
语法:for(expr1;expr2;expr3) statement
for(variable assignment;condition;iteration process) {for-body}
~]# awk '/^[[:space:]]*linux16/{for(i=1;i<=NF;i++) {print $i,length($i)}}' /etc/grub2.cfg
特殊用法:
能够遍历数组中的元素
语法:for(var in array) {for-body}
⑤switch语句
语法:switch(expression) {case VALUE1 or /REGEXP/:statement; case VALUE2 or /REGEXP2/:statement; ...; default:statement}
⑥break和continue
break [n]
continue 提前结束本来循环,进入下一字段
⑦next
awk实际上进行了两次循环
next可以提前结束对本行的处理而直接进入下一行
~]# awk -F: '{if($3%2!=0) next; print $1,$3}' /etc/passwd
8.array
关联数组:array[“index-expression”]
index-expression:
①可使用任意字符串;字符串要使用双引号
②如果某数组元素事先不存在,在引用时,awk会自动创建此元素,并将其值初始化为“空串”
P.S. 若要判断数组中是否存在某元素,要使用"index in array"格式进行
若要遍历数组中的每个元素,要使用for循环
for(var in array) {for-body}
~]# awk 'BEGIN{weekdays["mon"]="Monday";weekdays["tue"]="Tuesday";for(i in weekdays) {print weekdays[i]}}' 和bash不一样,索引记得不要加$
P.S. var会遍历array的每个索引
常见用法:统计某一类值出现的次数
~]# netstat -tan | awk '/^tcp\>/{state[$NF]++}END{for(i in state) { print i,state[i]}}'
state["LISTEN"]++
state["ESTABLISHED"]++
~]# awk '{ip[$1]++}END{for(i in ip) {print i,ip[i]}}' /var/log/httpd/access_log
练习1:统计/etc/fstab文件中每个文件系统类型出现的次数
~]# awk '/^UUID/{fs[$3]++}END{for(i in fs) {print i,fs[i]}}' /etc/fstab
练习2:统计指定文件中每个单词出现的次数
要做行内字段遍历
~]# awk '{for(i=1;i<=NF;i++){count[$i]++}}END{for(i in count) {print i,count[i]}}' /etc/fstab
9.函数
①内置函数
数值处理:
rand():返回0和1之间一个随机数
字符串处理:
length([s]):返回指定字符串的长度
sub(r,s,[t]):以r表示的模式来查找t所表示的字符中的匹配的内容,并将其第一次出现替换为s所表示的内容
gsub(r,s,[t]):以r表示的模式来查找t所表示的字符中的匹配的内容,并将其所有出现均替换为s所表示的内容
split(s,a[,r]):以r为分隔符切割字符s,并将切割后的结果保存至a所表示的数组(从1开始编号)中
~]# netstat -tan | awk '/^tcp\>/{split($5,ip,":");count[ip[1]]++}END{for (i in count) {print i,count[i]}}'
②自定义函数
function name(arg1, arg2, arg3..)
{
statement
}
回顾
grep
grep:文本过滤器
PATTERN
REGEXP:
BRE、ERE
BRE:
字符匹配:., [], [^]
次数匹配:*, \?, \+, \{m\}, \{m,n\}
位置锚定:^, $, \<, \b, \>, \b
分组:\(\)
后向引用:\1, \2, ...
vim, sed, awk, nginx,
egrep及扩展的正则表达式
egrep = grep -E
egrep [OPTIONS] PATTERN [FILE...]
扩展正则表达式的元字符:
字符匹配:
.
[]
[^]
次数匹配:
*
?:0或1次;
+:1次或多次;
{m}:匹配m次;
{m,n}:至少m,至多n次;
锚定:
^
$
\<, \b
\>, \b
分组:
()
后向引用:\1, \2, ...
或者:
a|b
C|cat:C或cat
sed
sed [options] 'SCRIPT' FILE...
编辑命令:d, p, w, r, a, i, c, s, =,
n, N, h, H, g, G, p, P, x, D
awk
-F, -v
print, printf
$i, FS, RS, NF, NR, ARGC, ARGV
+, -, *, /, ^, %
>, <, ==, !=
~, !~
&&, ||, !
BEGIN, END
for( var in array )
split(s,a[,r])
参考资料:
①马哥随堂笔记
②《Linux Shell 脚本攻略》
③《Shell 脚本学习指南》
注:诚恳欢迎读者对本文提出批评意见,若发现存在错误,我定第一时间修改。如果读者觉得文章对您有帮助,欢迎点赞鼓励一下哟٩(๑❛ᴗ❛๑)۶。