22.文本_ 正则、grep、cut、sed、awk

1、正则表达式

正则表达式是基于模式匹配的文本处理技术的关键所在。想要有效地运用正则表达式,就必须对其有一个基本的理解。

会用ls的用户应该都熟悉通配符模式。通配符可以运用在很多场景中,但是对于文本处理而言,功能还远远不够。正则表达式允许你更精细地描述模式。

[a-z0-9_]+@[a-z0-9]+\.[a-z]+.就是一个典型的能够匹配电子邮件地址的正则表达式。

正则表达式的更多细节请参考:http://www.linuxforu.com/2011/04/sed-explained-part-1/

a. 位置标记

位置标记锚点(position marker anchor)是标识字符串位置的正则表达式。默认情况下,正则表达式所匹配的字符可以出现在字符串中任何位置。

^

以……开头

^tux 能够匹配以tux起始的行

$

以……结尾

tux$ 能够匹配以tux结尾的行

b. 标识符

A字符

正则表达式必须匹配该字符

A能够匹配字符A

.

匹配任意一个字符

Hack.能够匹配Hackl和Hacki,但是不能匹配Hackl2或Hackil

[]

匹配中括号内的任意一个字符。中括号内可以是一个字符组或字符范围

coo[kl]能够匹配cook或cool,[0-9]匹配任意单个数字

[^]

匹配不在中括号内的任意一个字符。中括号内可以是一个字符组或字符范围

A[^0-9]匹配A以及随后除数字外的任意单个字符

c. 数量修饰符

一个标识符可以出现一次、多次或是不出现。数量修饰符定义了模式可以出现的次数

?

匹配之前的项1次或0次

colou?r 匹配color或colour,不匹配colouur

+

匹配之前的项1次或多次

Rollno-9+ 匹配Rollno-99和Rollno-9,不匹Rollno-

\*

匹配之前的项0次或多次

co*l 匹配cl、col和coool

{n}

匹配之前的项n次

[0-9]{3} 匹配任意的三位数,[0-9]{3} = [0-9][0-9][0-9]

{n,}

匹配之前的项至少需要n次

[0-9]{2,} 匹配任意一个两位或更多位的数字

{n,m}

匹配之前项的最小次数至最大次数

[0-9]{2,5} 匹配两位数到五位数之间的任意一个数字

d. 匹配特殊字符

()

将括号中的内容视为一个整体

ma(tri)?x 能够匹配max或matrix

|

指定了一种选择结构,可以匹配 | 两边

Oct (1st | 2nd) 能够匹配Oct 1st或Oct 2nd

\

转义字符可以转义之前介绍的特殊字符

a\.b 能够匹配a.b,但不能匹配ajb。因为 \忽略了.的特殊意义

--------------------------------------------

# 匹配任意单词的正则表达式
( +[a-zA-Z]+ +)
  • 开头的+表示需要匹配一个或多个空格。
  • 字符组[a-zA-Z]用于匹配所有的大小写字母。
  • 随后的+表示至少要匹配一个字母,多者不限。
  • 最后的+表示需要匹配一个或多个空格来终结单词。

这个正则表达式无法匹配句子末尾的单词。要想匹配句尾或是逗号前的单词,需要将正则表达式改写为:

( +[a-zA-Z]+[?,.]? +)
# 匹配IP地址
[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}

# 或者也可以使用[[:digit:]]表示数字:
[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}\.[[:digit:]]{1,3}
  • 这个正则表达式可以匹配文本中的IP地址,但是它并不检查地址的合法性。例如,形如123.300.1.1的IP地址可以被正则表达式匹配,但这却是一个非法的IP地址。

2、grep 命令

描述:用于文本搜索的神奇工具,能够接受正则表达式,生成各种格式的输出。

语法:grep [选项] “匹配规则”file

  • --color 可以在输出行中着重标记出匹配到的模式
  • -E 可以使grep使用扩展正则表达式,默认使用基础正则表达式。
  • -v 可以打印出不匹配match_pattern的所有行
  • -o 只输出匹配到的文本
  • -c 能够统计出匹配模式的文本行数
  • -n 可以打印出匹配字符串所在行的行号:
  • -b 可以打印出匹配出现在行中的偏移。配合选项-o可以打印出匹配所在的字符或字节偏移
  • -l 可以列出匹配模式所在的文件
  • -L 可以列出没有匹配模式所在的文件
  • -r 递归搜索
  • -i 在匹配模式时不考虑字符的大小写
  • -e 可以指定多个匹配模式
  • -f 可以读取文件并使用其中的模式(一个模式一行)
# 在stdin中搜索匹配特定模式的文本行
echo -e "this is a word\nnetx line" | grep word					

# 匹配扩展正则表达式
echo -e "this is a word\nnetx line" | grep -E  "[a-z]+"

# 只输出匹配到的文本
echo this is a line. | egrep -o "[a-z]+\."							# 输出-> line.

# 统计匹配到的行数
echo -e "1 2 3 4\nhello\n5 6" | egrep -c  "[0-9]"				# 输出-> 2

# 匹配出不包含 1.txt 的文件名
grep -L "test" duplicate_files unique_files 1.txt				# 输出-> 1.txt

# 在多级目录中对文本进行递归搜索
grep "text" . -R -n

# 指定多个匹配模式
echo this is a line of text | grep -o -e "this" -e "line"	# 输出 this \n line
3、cut 命令

描述:可用于处理使用固定宽度字段的文件、CSV文件或是由空格分隔的文件(例如标准日志文件)。

语法:cut [选项] file

  • -f 可以指定要提取的字段
  • -d 能够设置分隔符
  • -b 表示字节
  • -c 表示字符
  • --complement 显示出没有被-f指定的那些字段
  • --output-delimiter 可以指定输出分隔符
# 提取多个字段,就得给出由逗号分隔的多个字段编号
cut -f 2,4 student_data.txt

# 以;为分隔符,提取第二段
cut -f 2 -d ";" delimited_data.txt

固定列宽的报表在列与列之间都存在数量不等的空格。你没法根据字段的位置来提取值,但是可以根据字符位置提取。cut命令可以根据字节或者字符来指定选择范围。

22.文本_ 正则、grep、cut、sed、awk_第1张图片

# 打印第2个到第5个字符
cut -c 2-5 range_fields.txt

# 打印前2个字符
cut -c -2 range_fields.txt

cut range_fields.txt -c 1-3,6-9 --output-delimiter ","
4、sed 命令

描述:流编辑器 进行文本替换。

语法:sed [选项] [匹配模式] file

  • -i 使得用sed修改后的数据替换原始文件
  • -e 使用多个表达式
# 删除
# /d告诉sed不执行替换操作,而是直接删除匹配到的空行
sed '/^$/d' file

# 替换
# g标记可以使sed执行全局替换
sed 's/pattern/replace_string/g' file

# /#g标记可以使sed替换第N次出现的匹配
echo thisthisthisthis | sed 's/this/THIS/2g'	# 输出-> thisTHISTHISTHIS

# 使用指定的数字替换文件中所有3位数的数字
 sed -i 's/\b[0-9]\{3\}\b/NUMBER/g' sed_data.txt
  • 正则表达式\b[0-9]\{3\}\b用于匹配3位数字
  • \b表示单词边界。
a. 已匹配字符串标记:&

在sed中,我们可以用&指代模式所匹配到的字符串,这样就能够在替换字符串时使用已匹配的内容:

echo this is an example | sed 's/\w\+/[&]/g'	# 输出-> [this] [is] [an] [example]
  • \w\+匹配每一个单词,然后我们用[&]替换它。&对应于之前所匹配到的单词。
b. 子串匹配标记:\1

使用\#来指代出现在括号中的部分正则表达式(注:子模式)所匹配到的内容:

echo this is digit 7 in a number | sed 's/digit \([0-9]\)/\1/' 
# 输出-> this is 7 in a number
  • 这条命令将digit 7替换为7
  • 对于匹配到的第一个子串,其对应的标记是\1,匹配到的第二个子串是\2,往后以此类推。
echo hello WORLD | sed 's/\([a-z]\+\) \([A-Z]\+\)/\2 \1/'	# 输出-> WORLD hello
  • ([a-z]\+\)匹配第一个单词,\([A-Z]\+\)匹配第二个单词。\1和\2分别用来引用这两个单词。
  • 这种引用形式叫作向后引用(back reference)。在替换部分,它们的次序被更改为\2 \1,因此就呈现出了逆序的结果。
c. 组合多个表达式

可以利用管道组合多个sed命令,多个模式之间可以用分号分隔,或是使用选项-e PATTERN:

# 利用管道组合多个sed命令
sed 'expression' | sed 'expression'
# 多个模式之间可以用分号分隔
sed 'expression; expression'者
# 使用选项-e PATTERN
sed -e 'expression' -e 'expression'

echo abc | sed 's/a/A/' | sed 's/c/C/'
echo abc | sed 's/a/A/;s/c/C/'
echo abc | sed -e 's/a/A/' -e 's/c/C/'
5、awk 命令

描述:可以处理数据流。它支持关联数组、递归函数、条件语句等功能。

语法:awk 'BEGIN{ print "start" } pattern { commands } END{ print "end" }' file

  • -v 可以将外部值(并非来自stdin)传递给awk
  • -F 指定字段的分隔符

awk以逐行的形式处理文件。BEGIN之后的命令会先于公共语句块执行。对于匹配PATTERN的行,awk会对其执行PATTERN之后的命令。最后,在处理完整个文件之后,awk会执行END之后的命令。

# 输出文件行数
awk 'BEGIN{ i=0 } { i++ } END{ print i}' anaconda-ks.cfg 
  • BEGIN语句块在awk开始从输入流中读取行之前被执行。这是一个可选的语句块,诸如变量初始化、打印输出表格的表头等语句通常都可以放在BEGIN语句块中。
  • END语句块和BEGIN语句块类似。它在awk读取完输入流中所有的行之后被执行。像打印所有行的分析结果这种常见任务都是在END语句块中实现的。
  • pattern关联的语句块。这个语句块同样是可选的。如果不提供,则默认执行{ print},即打印所读取到的每一行。awk对于读取到的每一行都会执行该语句块。这就像一个用来读取行的while循环,在循环体中提供了相应的语句。
  • 每读取一行,awk就会检查该行是否匹配指定的模式。模式本身可以是正则表达式、条件语句以及行范围等。如果当前行匹配该模式,则执行{ }中的语句。

print能够接受参数。这些参数以逗号分隔,在打印参数时则以空格作为参数之间的分隔符。在awk的print语句中,双引号被当作拼接操作符(concatenation operator)使用。

echo | awk '{ var1="v1"; var2="v2"; var3="v3"; print var1,var2,var3 }' # 输出-> v1 v2 v3

# 拼接的使用方法,输出-> v1-v2-v3
echo | awk '{ var1="v1"; var2="v2"; var3="v3"; print var1 "-" var2 "-" var3 }' 
a. 特殊变量

变量

含义

NR

表示记录编号,当awk将行作为记录时,该变量相当于当前行号。

NF

表示字段数量,在处理当前记录时,相当于字段数量。默认的字段分隔符是空格。

$0

该变量包含当前记录的文本内容。

$1

该变量包含第一个字段的文本内容。

$2

该变量包含第二个字段的文本内容

echo -e "line1 f2 f3\nline2 f4 f5\n line3 f6 f7" | \
awk '{ print "Line no:"NR",No of fields:"NF, "$0="$0, "$1="$1,"$2="$2,"$3="$3}'
  • 可以用print $NF打印一行中最后一个字段,用 $(NF-1)打印倒数第二个字段,其他字段以此类推。
  • awk也支持printf()函数,其语法和C语言中的同名函数一样。

seq 5 | awk 'BEGIN { sum=0; print "Summation:" } \
{print $1"+"; sum+=$1} END { print "=="; print sum}'

22.文本_ 正则、grep、cut、sed、awk_第2张图片

b. -v 选项

描述:借助选项-v,我们可以将外部值(并非来自stdin)传递给awk

VAR=10000
echo | awk -v VARIABLE=$VAR '{print VARIABLE}'		// 输出->10000


# 将多个外部变量传递给awk
var1="Variables1";var2="Variables2"
echo | awk '{print v1, v2}' v1=$var1 v2=$var2 		// 输出->Variables1 Variables2

# 输入来自于文件而非标准输入时
 awk '{ print v1,v2 }' v1=$var1 v2=$var2 filename
c. getline 读行

awk默认读取文件中的所有行。如果只想读取某一行,可以使用getline函数。它可以用于在BEGIN语句块中读取文件的头部信息,然后在主语句块中处理余下的实际数据。

该函数的语法为:getline var。变量var中包含了特定行。如果调用时不带参数,我们可以用$0、$1和$2访问文本行的内容。

seq 5 | awk 'BEGIN { getline; print "Read ahead first line", $0} { print $0 }'

22.文本_ 正则、grep、cut、sed、awk_第3张图片

d. 过滤模式

可以为需要处理的行指定一些条件:

awk 'NR < 5'   			 # 行号小于5的行
awk 'NR==1,NR==4'    # 行号在1到5之间的行
awk '/linux/'    		 # 包含模式为linux的行(可以用正则表达式来指定模式)
awk '!/linux/'    	 # 不包含模式为linux的行
e. 字段分隔符

默认的字段分隔符是空格。我们也可以用选项-F指定不同的分隔符:

echo | awk 'BEGIN {FS=":"} { "grep root /etc/passwd"| getline; print $1,$6 }'

awk -F ":" '{ print $NF }' /etc/passwd
awk 'BEGIN { FS=":" }{ print $NF }' /etc/passwd

22.文本_ 正则、grep、cut、sed、awk_第4张图片

在BEGIN语句块中可以用OFS="delimiter"设置输出字段分隔符。

f. 使用循环

在awk中可以使用for循环,其格式与C语言中的差不多:for(i=0;i<10;i++) { print $i ; }

另外awk还支持列表形式的for循环,也可以显示出数组的内容:for(i in array) { print array[i]; }

awk 'BEGIN{FS=":"} {nam[$1]=$5} END{for(i in nam){print i, name[i]}}' /etc/passwd
g.内置函数

awk有很多内建的字符串处理函数。

内置字符串函数

含义

length(string)

返回字符串string的长度

index(string, search_string)

返回search_string在字符串string中出现的位置

split(string, array, delimiter)

以delimiter作为分隔符,分割字符串string,将生成的字符串存入数组array

substr(string, start-position, end-position)

返回字符串string中以start-position和end-position作为起止位置的子串

sub(regex, replacement_str, string)

将正则表达式regex匹配到的第一处内容替换成replacment_str

gsub(regex, replacement_str, string)

和sub()类似。不过该函数会替换正则表达式regex匹配到的所有内容。

match(regex, string)

检查正则表达式regex是否能够在字符串string中找到匹配。如果能够找到,返回非0值;否则,返回0。match()有两个相关的特殊变量,分别是RSTART和RLENGTH。变量RSTART包含了匹配内容的起始位置,而变量RLENGTH包含了匹配内容的长度。

你可能感兴趣的:(Linux,Shell,脚本攻略,Shell,Linux)