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 |
. | 匹配任意一个字符 |
|
[] | 匹配中括号内的任意一个字符。中括号内可以是一个字符组或字符范围 | 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命令可以根据字节或者字符来指定选择范围。
# 打印第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}'
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 }'
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
在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包含了匹配内容的长度。 |