正则表达式是基于模式匹配的文本处理技术的关键所在。想要有效地运用正则表达式,就必须对其有一个基本的理解。
会用ls的用户应该都熟悉通配符模式。通配符可以运用在很多场景中,但是对于文本处理而言,功能还远远不够。正则表达式允许你更精细地描述模式。
[a-z0-9_]+@[a-z0-9]+\.[a-z]+.
就是一个典型的能够匹配电子邮件地址的正则表达式。
正则表达式的更多细节请参考:http://www.linuxforu.com/2011/04/sed-explained-part-1/
位置标记锚点(position marker anchor)是标识字符串位置的正则表达式。默认情况下,正则表达式所匹配的字符可以出现在字符串中任何位置。
^ |
以……开头 |
^tux 能够匹配以tux起始的行 |
$ |
以……结尾 |
tux$ 能够匹配以tux结尾的行 |
A字符 |
正则表达式必须匹配该字符 |
A能够匹配字符A |
. |
匹配任意一个字符 |
|
[] |
匹配中括号内的任意一个字符。中括号内可以是一个字符组或字符范围 |
coo[kl]能够匹配cook或cool,[0-9]匹配任意单个数字 |
[^] |
匹配不在中括号内的任意一个字符。中括号内可以是一个字符组或字符范围 |
A[^0-9]匹配A以及随后除数字外的任意单个字符 |
一个标识符可以出现一次、多次或是不出现。数量修饰符定义了模式可以出现的次数
? |
匹配之前的项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} 匹配两位数到五位数之间的任意一个数字 |
() |
将括号中的内容视为一个整体 |
ma(tri)?x 能够匹配max或matrix |
| |
指定了一种选择结构,可以匹配 | 两边 |
Oct (1st | 2nd) 能够匹配Oct 1st或Oct 2nd |
\ |
转义字符可以转义之前介绍的特殊字符 |
a\.b 能够匹配a.b,但不能匹配ajb。因为 \忽略了.的特殊意义 |
--------------------------------------------
# 匹配任意单词的正则表达式
( +[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}
描述:用于文本搜索的神奇工具,能够接受正则表达式,生成各种格式的输出。
语法:grep [选项] “匹配规则”file
# 在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
描述:可用于处理使用固定宽度字段的文件、CSV文件或是由空格分隔的文件(例如标准日志文件)。
语法:cut [选项] file
# 提取多个字段,就得给出由逗号分隔的多个字段编号
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 ","
描述:流编辑器 进行文本替换。
语法:sed [选项] [匹配模式] file
# 删除
# /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
在sed中,我们可以用&指代模式所匹配到的字符串,这样就能够在替换字符串时使用已匹配的内容:
echo this is an example | sed 's/\w\+/[&]/g' # 输出-> [this] [is] [an] [example]
使用\#来指代出现在括号中的部分正则表达式(注:子模式)所匹配到的内容:
echo this is digit 7 in a number | sed 's/digit \([0-9]\)/\1/'
# 输出-> this is 7 in a number
echo hello WORLD | sed 's/\([a-z]\+\) \([A-Z]\+\)/\2 \1/' # 输出-> WORLD hello
可以利用管道组合多个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/'
描述:可以处理数据流。它支持关联数组、递归函数、条件语句等功能。
语法:awk 'BEGIN{ print "start" } pattern { commands } END{ print "end" }' file
awk以逐行的形式处理文件。BEGIN之后的命令会先于公共语句块执行。对于匹配PATTERN的行,awk会对其执行PATTERN之后的命令。最后,在处理完整个文件之后,awk会执行END之后的命令。
# 输出文件行数
awk 'BEGIN{ i=0 } { i++ } END{ print i}' anaconda-ks.cfg
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 }'
变量 |
含义 |
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}'
seq 5 | awk 'BEGIN { sum=0; print "Summation:" } \
{print $1"+"; sum+=$1} END { print "=="; print sum}'
描述:借助选项-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
awk默认读取文件中的所有行。如果只想读取某一行,可以使用getline函数。它可以用于在BEGIN语句块中读取文件的头部信息,然后在主语句块中处理余下的实际数据。
该函数的语法为:getline var。变量var中包含了特定行。如果调用时不带参数,我们可以用$0、$1和$2访问文本行的内容。
seq 5 | awk 'BEGIN { getline; print "Read ahead first line", $0} { print $0 }'
可以为需要处理的行指定一些条件:
awk 'NR < 5' # 行号小于5的行
awk 'NR==1,NR==4' # 行号在1到5之间的行
awk '/linux/' # 包含模式为linux的行(可以用正则表达式来指定模式)
awk '!/linux/' # 不包含模式为linux的行
默认的字段分隔符是空格。我们也可以用选项-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"设置输出字段分隔符。
在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
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包含了匹配内容的长度。 |