正则三剑客

基础正则

Basic Regular Expression, BRE

^ 以……开头

$ 以……结尾

^$ 空行(这一行中没有任何元素)

. 代表任何一个字符(但是不包括空行)

\ 转译符

* 前一个字符连续出现0次或0次以上

.* 所有内容、任意内容(包含空行)

1
grep '^.*o' test.txt  # 匹配从开头第一个字符到字符o的所有内容(贪婪匹配,会直接匹配到最后一个o)

[] 匹配指定的任何一个字符

1
2
3
4
grep '[abc]' test.txt  # 匹配a或b或c
# [a-z] [A-Z] [0-9] [a-zA-Z0-9] [a-Z0-9]
# 中括号内的字符一般都会被去除特殊含义,比如 [a-z,0-9] 表示匹配小写字母或逗号或数字
# [^abc] 中括号里面第一个^表示取反,即除了abc以外的字符

拓展正则

Extended Regular Expression, ERE

+ 前一个字符连续出现1次或1次以上

| 或者 比如 ‘qizidog|qizizhou’ 匹配qizidog或者qizizhou(中括号是或者单个字符,竖线是或者两个部分)

() 一般来说有两种用法:被括起来的部分相当于一个整体;后/反向引用

1
2
3
4
# 以下三种写法均表示匹配oldboy或者oldbey
grep -E 'oldboy|oldbey' test.txt
grep -E 'oldb[oe]y' test.txt
grep -E 'oldb(o|e)y' test.txt

{} ‘o{m,n}’ 前一个字母o,至少连续出现了m次,至多连续出现n次(贪婪匹配);’o{m}’ 前一个字母o,恰好连续出现了m次;’o{m,}’ 前一个字母o,至少连续出现了m次;’o{,n}’ 前一个字母o,至多连续出现了n次

? 前一个字符出现了0次或1次

三剑客

三剑客成员

command function 场景
grep 过滤 过滤速度很快
sed 替换、取行 需要替换/修改文件内容,取出某个范围的内容
awk 取列、统计 取列、比较、统计计算

grep

1
2
3
4
5
6
7
8
9
10
11
12
grep -A  # after,A5表示显示匹配对象后5行;在匹配上的对象后面加上一个分隔符号(默认是$)
-B # before,B5表示显示匹配对象前5行
-C # context,C5表示显示匹配对象前后各5行
-E # 使用拓展正则(grep默认使用基础正则),也可以使用egrep命令
-c # 统计被匹配项出现的行数,类似于wc -l
-e # 基础正则表达式
-i # 忽略大小写
-n # 显示行号
-v # 反向匹配(有匹配字符的不要)
-w # 精确匹配,也可以通过在正则中使用转译符`\b`或者`\<`和`\>`来指定边界
-o # 只输出匹配上的部分(查看匹配过程,一次一行)
-r # 递归查找每一个子文件夹

惯用写法:ps -ef | grep 'python' | grep -v grep | wc -l 检查有多少个python后台进程(排除了grep自身的这一个进程),也可以用这种写法 ps -ef | grep '[p]ython' | wc -l,通过一个中括号巧妙地避免了grep的过滤项和自身重名。

举例:取轨迹数据日志的订单id:grep -E '[0-9a-z]{32}' traj-0110.log

sed

1
2
sed -n  # 取消默认输出(不匹配的就不输出,类似于grep的-o)
-E或-r # 使用拓展正则

示范命令:sed -r 's#oldboy#oldgirl#g' oldboy.txt

sed命令核心功能:增删改查

符号 功能
p 显示 print
d 删除 delete
cai 增加 c/a/i
s 替换 substitute

sed命令执行过程:“找谁干啥”

查找p

查找格式 功能
‘1p’ ‘2p’ 指定行号进行查找。’$p’表示最后一行
‘1,5p’ 指定行号范围进行查找。’4,$p’表示从第4行到最后一行
‘/hello/p’ 类似于grep过滤,//里面可以写正则
‘/10:00/,/11:00/p’ 输出从包含10:00到包含11:00的所有行(找日志常用)
‘2,/hello/p’ 混合使用,输出从第2行开始到包含hello之间的所有行

举例:sed -nE '/18:1[0-9]:[0-9]{2}/,/18:1[0-9]:[0-9]{2}/p' traj-0110.log 找出[18:10, 18:20)范围内的所有日志。

⚠️注意,使用范围查询时,如果起始行找不到匹配,那么无论结束行是否能够被匹配,结果输出都为空(因为没有开始);如果找到了起始行,但没有找到结束行,那么从起始行开始一直到最后的所有内容都会被输出。

删除d

删除格式 功能
‘1d’ ‘2d’ 指定行号进行删除。’$p’表示最后一行
‘1,5d’ 指定行号范围进行删除。’4,$p’表示从第4行到最后一行
‘/hello/d’ 删除包含hello的行,//里面可以写正则
‘/10:00/,/11:00/d’ 删除从包含10:00到包含11:00的所有行(找日志常用)
‘2,/hello/d’ 混合使用,删除从第2行开始到包含hello之间的所有行

删除操作是不会直接修改原始文件的。

举例:删除(不显示)文件中的空行和注释行:

  • grep -vE '^$|^#' /etc/ssh/ssh_config
  • sed -E '/^$|^#/d' /etc/ssh/ssh_config
  • sed -nE '/^$|^#/!p' /etc/ssh/ssh_config !表示取反,即遇到空行和注释行则不显示输出

增加cai

类型 功能
c replace,替代这一行
a append,在指定行的下一行追加内容
i insert,在指定行的前一行插入内容

举例:向config文件中追加一些配置内容:

  • cat >> config << 'EOF' 这个方法会直接修改原始文件,直接把后面输的内容追加到了文件的最后。
    UseDNS no
    PermitRootLogin no
    EOF
  • sed '$a UseDNS no\nPermitRootLogin no' config $a后面的那一个空格加不加都可以,加上更易读一点。此方法不会修改原始文件,而是将修改的结果输出到屏幕,可以通过重定向来写入新的文件。

替换s

替换格式 功能
s###g 中间是三个相同的符号就行,比如3个#、/、2都可以。
井号之间分别放替换前和替换后的内容。
  • g:global全局替换的意思,如果不加g,只会替换第一个满足条件的值。
  • 后向引用:先保护,再使用。用小括号把要引用的内容括起来,在替换项处再通过反斜杠+数字来引用。

举例:

  • 删除所有的数字:sed 's#[0-9]##g' traj.log
  • 将所有的uuid用两个五角星包裹起来:sed -E 's#([0-9a-z]{32})#⭐️\1⭐️#g' traj.log
  • 取出指定网卡的ip地址:
    • ip addr show eth0 | sed -n '3p' | sed -E 's#.*t (.*)/.*#\1#g' 其中第一段可以简写为 ip a s eth0
    • ip a s eth0 | sed -nE '3s#.*t (.*)/.*#\1#gp' 后两段简写到了一起。
  • 取轨迹数据日志的订单id:sed -nE 's#.*: ([0-9a-z]{32}) .*#\1#gp' traj-0110.log

⚠️mac中的sed命令是基于BSD的,而linux中的sed是基于GNU的,在mac中使用sed命令的时候需要换行写,体验不怎么样,所以还是装一个GNU的来代替原本的sed比较好。

1
2
3
brew install gnu-sed
# 把别名规则写入配置文件(如果不写,也可以直接使用gsed)
echo 'alias sed=gsed' >> ~/.zshrc

awk

awk语法与c语言类似,是一种处理文本文件的语言,是一个强大的文本分析工具。

awk公式:awk + 选项 + ‘pattern{action}’

使用awk需要了解行和列两个基本的概念:

  • 行:record,记录,每一行默认通过回车分割。
  • 列:field,字段,每一列默认通过空格、连续空格、制表符分割。
1
2
awk -F  # 指定列分隔符(默认支持拓展正则!)
-v # 修改awk变量

示范命令:awk -F, 'BEGIN{print "name"}NR==2{print $2}END{print "end of file"}' oldboy.txt

awk内置变量

var 说明
NR Number of Record 行号
NF Number of Field 列数
FS Field Separator 字段分隔符(-F:相当于-v FS=:)
OFS Output Field Separator 输出字段分隔符

取行

方法 说明
NR==1 取第一行
NR>=1&&NR<=10 取第1-10行
/hello/ 取包含“hello”的行
/10:00/,/11:00/ 取包含“10:00”到包含“11:00”之间的行

取列

  • -F 参数:指定列分隔符
  • $n:取第n列,其中 $0 表示所有列,$NF 表示最后一列

举例:

  • 取出第5列和第7列:ls -al . | awk '{print $5,$7}' | column -t
  • 同时指定行数:ls -al . | awk 'NR==3||NR==5{print $5,$7}' | column -t
  • 取出所有的用户名:grep -vE '^$|^#' /etc/passwd | awk -F : '{print $1}'
  • 取出所有的用户名和家目录,并用⭐️分隔:grep -vE '^$|^#' /etc/passwd | awk -F : '{print $1"⭐"$NF}'
  • 将第1列和最后一列的位置交换输出(分隔符维持原样):grep -vE '^$|^#' /etc/passwd | awk -F : -v OFS=: '{print $NF,$2,$3,$4,$5,$6,$1}'
  • 取出指定网卡的ip地址:ip a s eth0 | awk -F"[ /]+" 'NR==3{print $3}'

⚠️awk后的条件应该使用单引号包裹,因为双引号中的$代表变量,如果使用双引号则需要在取列时加上转译符。此外,awk中的字母都会被识别成变量,如果只是想使用字符串,需要将字母用双引号包裹起来。

awk模式匹配——正则匹配

通过模式匹配可以直接抓取某一列满足特定条件的信息,默认支持拓展正则。

  • ~ 表示服从后续的正则
  • !~ 表示不服从后续的正则

举例:找到第3列以1开头的行,输出这行的第1列和第3列

  • grep -vE '^$|^#' /etc/passwd | awk -F: '$3~/^1/{print $1,$3}'

awk模式匹配——范围模式

  • /pattern1/,/pattern2/:从满足pattern1的行到满足pattern2的行
  • NR==2,NR==10:从第2行到第10行

awk模式匹配——特殊模式

特殊模式 应用场景
BEGIN{} 1)进行简单统计计算,一般不涉及读取文件
2)添加表头
3)用来定义awk变量(很少用,因为可以用-v)
END{} 1)输出统计结果
2)输出数组结果

举例:

  • 统计空行和注释行的行数:awk '/^$|^#/{i++}END{print i}' /etc/service i是变量名,可以随便取
  • 求和1-100:seq 100 | awk '{sum+=$1}END{print sum}' sum是变量名,可以随便取

awk数组

应用场景: 统计每个IP出现的次数、统计每种状态码出现的次数、统计每个AP消耗的流量… 类似于group by。

shell数组 awk数组
形式 array[0]=hello array[1]=world array[0]=hello array[1]=world
使用 echo ${array[0]} ${array[1]} print array[0] array[1]
遍历 for i in ${array[*]}
do
echo $i
done
for(i in array)
print array[i]

举例:

  • 输出hello world:awk 'BEGIN{a[0]="hello";a[1]="world"; for(i in a) print a[i]}'
1
2
3
4
5
6
7
8
9
10
11
12
# 处理下文件内容,统计各域名出现的次数并排序
http://www.etiantian.org/index.html
http://www.etiantian.org/1.html
http://post.etiantian.org/index.html
http://mp3.etiantian.org/index.html
http://www.etiantian.org/3.html
http://post.etiantian.org/2.html

# 即统计下面各域名出现的次数
www.etiantian.org
post.etiantian.org
mp3.etiantian.org
  • grep -v '^#' test | awk -F "/|//" '{arr[$2]++}END{for(i in arr) print i,arr[i]}' 通过grep去除注释信息
  • sed -n '2,$p' test | awk -F "/+" '{arr[$2]++}END{for (i in arr) print i,arr[i]}' 通过sed去除第一行的注释信息
  • 在以上处理的基础上再追加一个管道排序:xxx | sort -rnk 2,r代表逆序,n是数字,k指定第2列

条件、循环

语法和java基本一致。

举例:

判断磁盘空间是否充足:df -h | awk -F"[ %]+" 'NR>1{if($5>=70) {print $1,$5,"❌"} else {print $1,$5,"✅"} }'

统计这段话中字符数不小于4的单词:

1
2
3
# once upon a time, there is a dog called qizidog.

echo once upon a time, there is a dog called qizidog. | awk -F"[. ]" '{for(i=1;i<=NF;i++) {if(length($i)>=4) print $i,length($i)} }'

笔记整理自:https://www.bilibili.com/video/BV1244y1e73a

人剑合一

find

1
2
3
find -f dir_name -name 'pattern'  # 根据文件名在指定目录下搜索文件
find ~/Pictures -newer ~/Pictures/青羊宫.png -name '*.png' # 寻找比指定文件新的满足条件的文件
find . -regex '.*\.[c|h]' # 使用正则表达式搜索以.c或.h结尾的文件

ack

ack在执行搜索的过程中会自动忽略主流版本控制软件的相关目录,比如 .git.svn

1
2
ack [options] PATTERN [FILE...]
ack -f [options] [DIRECTORY...]

mac的ack支持 --range-start--range-end 两个参数。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!