Linux_Shell脚本学习第四章-让文本飞(上)

一、使用正则表达式

1.1 正则表达式的规则

1.1.1 位置标记

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

1.1.2 标识符

标识符是正则表达式的基础组成部分。它定义了那些为了匹配正则表达式,必须存在(或不
存在)的字符,如下图。
在这里插入图片描述

1.1.3 数量修饰符

一个标识符可以出现一次、多次或是不出现。数量修饰符定义了模式可以出现的次数,如下图。
在这里插入图片描述

1.1.4 其他一些特殊字符可以调整正则表达式的匹配方式

在这里插入图片描述

1.2 正则表达式的例子

1.2.1 能够匹配任意单词的正则表达式

( +[a-zA-Z]+ +)

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

1.2.2 匹配句尾或是逗号前的单词

( +[a-zA-Z]+[?,.]? +)

[?,.]?表示仅需要匹配问号、逗号或点号中的一个。

1.2.3 匹配ip地址

[0-9]{1,3}\.[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}

1.3 处理特殊字符

正则表达式用$、^、.、*、+、{以及}等作为特殊字符。但是如果我们希望将这些字符作
为普通字符使用,应该怎么做呢?来看一个正则表达式:a.txt。
该正则表达式能够匹配字符a,然后是任意字符(由.负责匹配),接着是字符串txt。但是我们希望.能够匹配字面意义上的.,而非任意字符。因此需要在.之前加上一个反斜线\(这叫作“字符转义”)。这表明正则表达式希望匹配的是字面含义,而不是它所代表的特殊含义。因此,最终的正则表达式就变成了a.txt。

二、使用grep 在文件中搜索文本

2.1 在stdin中搜索匹配特定模式的文本行

$ echo -e "this is a word\nnext line" | grep word
this is a word

2.2 在文件中搜索匹配特定模式的文本行

$ grep pattern filename
this is the line containing pattern

或者

$ grep "pattern" filename
this is the line containing patter

2.3 在多个文件中搜索匹配特定模式的文本行

$ grep "match_text" file1 file2 file3 ...

2.4 选项–color可以在输出行中着重标记出匹配到的模式。

尽管该选项在命令行中的放置位置没有强制要求,不过惯常作为第一个选项出现。

$ grep --color=auto word filename
this is the line containing word

2.5 grep命令默认使用基础正则表达式

这是先前描述的正则表达式的一个子集。选项-E可以使grep使用扩展正则表达式。也可以使用默认启用扩展正则表达式的egrep命令

$ grep -E "[a-z]+" filename

或者

$ egrep "[a-z]+" filename

2.6 选项-o可以只输出匹配到的文本

$ echo this is a line. | egrep -o "[a-z]+\."
line

2.7 选项-v可以打印出不匹配match_pattern的所有行

$ grep -v match_pattern file

2.8 选项-c能够统计出匹配模式的文本行数

$ grep -c "text" filename
10

需要注意的是-c只是统计匹配行的数量,并不是匹配的次数。例如:

$ echo -e "1 2 3 4\nhello\n5 6" | egrep -c "[0-9]"
2

尽管有6个匹配项,但egrep命令只输出2,这是因为只有两个匹配行。在单行中出现的
多次匹配只被计为一次。

2.9 统计文件中匹配项的数量

$ echo -e "1 2 3 4\nhello\n5 6" | egrep -o "[0-9]" | wc -l
6

2.10 选项-n可以打印出匹配字符串所在行的行号

$ cat sample1.txt
gnu is not unix
linux is fun
bash is art
$ cat sample2.txt
planetlinux
$ grep linux -n sample1.txt
2:linux is fun

或者

$ cat sample1.txt | grep linux -n

如果涉及多个文件,该选项也会随输出结果打印出文件名

$ grep linux -n sample1.txt sample2.txt
sample1.txt:2:linux is fun
sample2.txt:2:planetlinux

2.11 选项-b可以打印出匹配出现在行中的偏移

配合选项-o可以打印出匹配所在的字符或字节偏移。

$ echo gnu is not unix | grep -b -o "not"
7:not

字符在行中的偏移是从0开始计数,不是1。

2.12 选项-l可以列出匹配模式所在的文件

$ grep -l linux sample1.txt sample2.txt
sample1.txt
sample2.txt

和-l效果相反的选项是-L,它会返回一个不匹配的文件列表。

2.13 递归搜索多个文件

如果需要在多级目录中对文本进行递归搜索,可以使用下列命令

$ grep "text" . -R -n

命令中的.指定了当前目录。例如:

$ cd src_dir
$ grep "test_function()" . -R -n
./miscutils/test.c:16:test_function();

等价于

$ find . -type f | xargs grep "test_function()"

2.14 忽略模式中的大小写

选项-i可以在匹配模式时不考虑字符的大小写:

$ echo hello world | grep -i "HELLO"
hello

2.15 使用grep匹配多个模式

选项-e可以指定多个匹配模式

$ grep -e "pattern1" -e "pattern2"

上述命令会打印出匹配任意一种模式的行,每个匹配对应一行输出。例如:

$ echo this is a line of text | grep -o -e "this" -e "line"
this
line

可以将多个模式定义在文件中。选项-f可以读取文件并使用其中的模式(一个模式一行)

$ grep -f pattern_filesource_filename

例如

$ cat pat_file
hello
cool
$ echo hello this is cool | grep -f pat_file
hello this is cool

2.16 在grep搜索中指定或排除文件

grep可以在搜索过程中使用通配符指定(include)或排除(exclude)某些文件
使用–include选项在目录中递归搜索所有的 .c和 .cpp文件

$ grep "main()" . -r --include *.{c,cpp}

注意,some{string1,string2,string3}会被扩展成somestring1 somestring2 somestring3。

使用选项–exclude在搜索过程中排除所有的README文件:

$ grep "main()" . -r --exclude "README"

选项–exclude-dir可以排除目录:

$ grep main . -r -exclude-dir CVS

如果需要从文件中读取排除文件列表,使用–exclude-from FILE。

2.17 使用0值字节后缀的xargs与grep

xargs命令可以为其他命令提供命令行参数列表。当文件名作为命令行参数时,建议用0值字节作为文件名终结符,而非空格。因为一些文件名中会包含空格字符,一旦它被误解为终结符,那么单个文件名就会被视为两个(例如,New file.txt被解析成New和file.txt两个文件名)。这个问题可以利用0值字节后缀来避免。我们使用xargs从命令(如grep和find)中接收stdin文本。这些命令可以生成带有0值字节后缀的输出。为了指明输入中的文件名是以0值字节作为终结,需要在xargs中使用选项-0

创建测试文件:

$ echo "test" > file1
$ echo "cool" > file2
$ echo "test" > file3
$ grep "test" file* -lZ | xargs -0 rm

选项-l告诉grep只输出有匹配出现的文件名。选项-Z使得grep使用0值字节(\0)作为文件名的终结符。这两个选项通常都是配合使用的。xargs的-0选项会使用0值字节作为输入的分隔符.

2.18 grep的静默输出

有时候,我们并不打算查看匹配的字符串,而只是想知道是否能够成功匹配。这可以通过设置grep的静默选项(-q)来实现。在静默模式中,grep命令不会输出任何内容。它仅是运行命令,然后根据命令执行成功与否返回退出状态。0表示匹配成功,非0表示匹配失败。
下面这个脚本利用grep的静默模式来测试文件中是否有匹配文本:

  1 #!/bin/bash
  2 
  3 if [ $# -ne 2 ]
  4 then
  5         echo Usage: $0 matah_text filename
  6         exit 1
  7 fi
  8 
  9 match_text=$1
 10 filename=$2
 11 
 12 grep -q "$match_text" $filename
 13 
 14 if [ $? -eq 0 ]
 15 then
 16         echo The text exists in the file
 17 else
 18         echo Text does not exist in the file
 19 fi

这个silent_grep.sh脚本接受两个命令行参数:一个是需要匹配的单词(Student),另一个是文
件名(student_data.txt):

$ ./silent_grep.sh Student student_data.txt
The text exists in the file

2.19 打印出匹配文本之前或之后的行

选项-A可以打印匹配结果之后的行

$ seq 10 | grep 5 -A 3
5
6
7
8

选项-B可以打印匹配结果之前的行

$ seq 10 | grep 5 -B 3
2
3
4
5

选项-A和-B可以结合使用,或者也可以使用选项-C,它可以分别打印出匹配结果之前及之后的n行:

$ seq 10 | grep 5 -C 3
2
3
4
5
6
7
8

如果有多个匹配,那么使用–作为各部分之间的分隔

$ echo -e "a\nb\nc\na\nb\nc" | grep a -A 1
a
b
--
a
b

三、使用cut 按列切分文件

cut命令能够提取指定位置或列之间的字符。你可以指定每列的分隔符。在cut的术语中,每列被称为一个字段。

3.1 选项-f可以指定要提取的字段

cut -f FIELD_LIST filename

FIELD_LIST是需要显示的列。它由列号组成,彼此之间用逗号分隔。例如

$ cut -f 2,3 filename

该命令将显示第2列和第3列。

3.2 cut命令也能够从stdin中读取输入

制表符是字段默认的分隔符。对于没有使用分隔符的行,会将该行照原样打印出来。cut的选项-s可以禁止打印出这种行。下面的例子演示了如何从使用制表符作为分隔符的文件中提取列:

$ cat student_data.txt
No Name Mark Percent
1 Sarath 45 90
2 Alex 49 98
3 Anu 45 90

$ cut -f1 student_data.txt
No
1
2
3

3.3 要想提取多个字段,就得给出由逗号分隔的多个字段编号

$ cut -f2,4 student_data.txt
Name   Percent
Sarath 90
Alex   98
Anu    90

3.4 用 --complement选项显示出没有被-f指定的那些字段

下面的命令会打印出除第3列之外的所有列

$ cut -f3 --complement student_data.txt
No Name Percent
1 Sarath 90
2 Alex 98
3 Anu 90

3.5 选项-d能够设置分隔符

下面的命令展示了如何使用cut处理由分号分隔的字段

$ cat delimited_data.txt
No;Name;Mark;Percent
1;Sarath;45;90
2;Alex;49;98
3;Anu;45;90

$ cut -f2 -d";" delimited_data.txt
Name
Sarath
Alex
Anu

3.6 指定字段的字符或字节范围

固定列宽的报表在列与列之间都存在数量不等的空格。你没法根据字段的位置来提取值,但是可以根据字符位置提取。cut命令可以根据字节或者字符来指定选择范围。
除了逗号分隔的列表,cut可以接受下图列出的记法。
在这里插入图片描述
 -b 表示字节
 -c 表示字符
 -f 用于定义字段

例如:

$ cat range_fields.txt
abcdefghijklmnopqrstuvwxyz
abcdefghijklmnopqrstuvwxyz
abcdefghijklmnopqrstuvwxyz
abcdefghijklmnopqrstuvwxy

打印第2个到第5个字符

$ cut -c2-5 range_fields.txt
bcde
bcde
bcde
bcde

打印前2个字符
$ cut -c -2 range_fields.txt
ab
ab
ab
ab
若要用字节作为计数单位,可以将-c替换成-b。
选项–output-delimiter可以指定输出分隔符。在显示多组数据时,该选项尤为有用:

$ cut range_fields.txt -c1-3,6-9 --output-delimiter ","
abc,fghi
abc,fghi
abc,fghi
abc,fghi

四、使用sed 替换文本

4.1 使用另一个字符串来替换匹配模式

sed可以使用另一个字符串来替换匹配模式。模式可以是简单的字符串或正则表达式

$ sed 's/pattern/replace_string/' file

或者

$ cat file | sed 's/pattern/replace_string/'

4.1.1 选项-i会使得sed用修改后的数据替换原始文件

$ sed -i 's/text/replace/' file

4.1.2 g标记可以使sed执行全局替换

之前的例子只替换了每行中模式首次匹配的内容

$ sed 's/pattern/replace_string/g' file

/#g标记可以使sed替换第N次出现的匹配:

$ echo thisthisthisthis | sed 's/this/THIS/2g'
thisTHISTHISTHIS
$ echo thisthisthisthis | sed 's/this/THIS/3g'
thisthisTHISTHIS
$ echo thisthisthisthis | sed 's/this/THIS/4g'
thisthisthisTHIS

4.1.3 更改默认的分隔符/

sed命令会将s之后的字符视为命令分隔符。这允许我们更改默认的分隔符/:

sed 's:text:replace:g'
sed 's|text|replace|g'

如果作为分隔符的字符出现在模式中,必须使用\对其进行转义:

sed 's|te\|xt|replace|g'

|是出现在模式中被转义的分隔符。
将te|xt替换为replace

4.2 移除空行

空行可以用正则表达式 ^$ 进行匹配
最后的/d告诉sed不执行替换操作,而是直接删除匹配到的空行

$ sed '/^$/d' file

4.2 直接在文件中替换

如果将文件名传递给sed,它会将文件内容输出到stdout。要是我们想就地(in place)修改文件内容,可以使用选项-i:

$ sed 's/PATTERN/replacement/' -i filename

例如,使用指定的数字替换文件中所有3位数的数字

$ cat sed_data.txt
11 abc 111 this 9 file contains 111 11 88 numbers 0000

$ sed -i 's/\b[0-9]\{3\}\b/NUMBER/g' sed_data.txt
$ cat sed_data.txt
11 abc NUMBER this 9 file contains NUMBER 11 88 numbers 0000

上面的单行命令只替换了所有的3位数字。正则表达式\b[0-9]{3}\b用于匹配3位数字。[0-9]表示数字取值范围是从0到9。{3}表示匹配之前的数字3次。{3}中的\用于转义{和}。\b表示单词边界。

4.3 已匹配字符串标记(&)

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

$ echo this is an example | sed 's/\w\+/[&]/g'
[this] [is] [an] [example]

在这个例子中,正则表达式\w+匹配每一个单词,然后我们用[&]替换它。&对应于之前所匹配到的单词。

4.4 子串匹配标记(\1)

&指代匹配给定模式的字符串。我们还可以使用#来指代出现在括号中的部分正则表达式(注:子模式)所匹配到的内容:

$ echo this is digit 7 in a number | sed 's/digit \([0-9]\)/\1/'
this is 7 in a number

这条命令将digit 7替换为7。(pattern)用于匹配子串,在本例中匹配到的子串是7。子模式被放入使用反斜线转义过的()中。对于匹配到的第一个子串,其对应的标记是\1,匹配到的第二个子串是\2,往后以此类推。

$ echo seven EIGHT | sed 's/\([a-z]\+\) \([A-Z]\+\)/\2 \1/'
EIGHT seven

([a-z]+)匹配第一个单词,([A-Z]+)匹配第二个单词。\1和\2分别用来引用这两个单词。这种引用形式叫作向后引用(back reference)。在替换部分,它们的次序被更改为\2 \1,因此就呈现出了逆序的结果。

4.5 组合多个表达式

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

sed 'expression' | sed 'expression'

它等同于

$ sed 'expression; expression'

或者

$ sed -e 'expression' -e 'expression'

例如

$ echo abc | sed 's/a/A/' | sed 's/c/C/'
AbC
$ echo abc | sed 's/a/A/;s/c/C/'
AbC
$ echo abc | sed -e 's/a/A/' -e 's/c/C/'
AbC

4.6 引用

sed表达式通常用单引号来引用。不过也可以使用双引号。shell会在调用sed前先扩展双引号中的内容。如果想在sed表达式中使用变量,双引号就能派上用场了。
例如:

$ text=hello
$ echo hello world | sed "s/$text/HELLO/"
HELLO world

$text的求值结果是hello。

五、使用awk 进行高级文本处理

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

5.1 awk脚本的结构

awk脚本的结构如下:

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

awk命令也可以从stdin中读取输入。

awk脚本通常由3部分组成:BEGIN、END和带模式匹配选项的公共语句块(common statement
block)。这3个部分都是可选的,可以不用出现在脚本中。
awk以逐行的形式处理文件。BEGIN之后的命令会先于公共语句块执行。对于匹配PATTERN
的行,awk会对其执行PATTERN之后的命令。最后,在处理完整个文件之后,awk会执行END之后
的命令。

5.2 简单的awk脚本

简单的awk脚本可以放在单引号或双引号中:

awk 'BEGIN { statements } { statements } END { end statements }'

或者

awk "BEGIN { statements } { statements } END { end statements }"

下面的命令会输出文件行数:

$ awk 'BEGIN { i=0 } { i++ } END { print i}' filename

或者

$ awk "BEGIN { i=0 } { i++ } END { print i }" filename

5.3 awk命令工作方式

5.3.1 首先执行BEGIN { commands } 语句块中的语句

BEGIN语句块在awk开始从输入流中读取行之前被执行。这是一个可选的语句块,诸如变量
初始化、打印输出表格的表头等语句通常都可以放在BEGIN语句块中。

5.3.2 接着从文件或stdin中读取一行,如果能够匹配pattern,则执行随后的commands语句块。重复这个过程,直到文件全部被读取完毕。

最重要的部分就是和pattern关联的语句块。这个语句块同样是可选的。如果不提供,则默
认执行{ print },即打印所读取到的每一行。awk对于读取到的每一行都会执行该语句块。这
就像一个用来读取行的while循环,在循环体中提供了相应的语句。

每读取一行,awk就会检查该行是否匹配指定的模式。模式本身可以是正则表达式、条件语
句以及行范围等。如果当前行匹配该模式,则执行{ }中的语句。

模式是可选的。如果没有提供模式,那么awk就认为所有的行都是匹配的:

5.3.3 当读至输入流末尾时,执行END { commands } 语句块。

END语句块和BEGIN语句块类似。它在awk读取完输入流中所有的行之后被执行。像打印所有
行的分析结果这种常见任务都是在END语句块中实现的。

5.3.4 例子

$ echo -e "line1\nline2" | awk 'BEGIN { print "Start" } { print } END { print "End" } '
Start
line1
line2
End

当使用不带参数的print时,它会打印出当前行。

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

$ echo | awk '{ var1="v1"; var2="v2"; var3="v3"; print var1,var2,var3 ; }'

该命令输出如下:

v1 v2 v3

echo命令向标准输出写入一行,因此awk的 { } 语句块中的语句只被执行一次。如果awk的输入中包含多行,那么 { } 语句块中的语句也就会被执行相应的次数。
拼接的使用方法如下:

$ echo | awk '{ var1="v1"; var2="v2"; var3="v3"; print var1 "-" var2 "-" var3 ; }'

该命令输出如下:

v1-v2-v3

5.4 特殊变量

以下是awk可以使用的一些特殊变量。
 NR:表示记录编号,当awk将行作为记录时,该变量相当于当前行号。
 NF:表示字段数量,在处理当前记录时,相当于字段数量。默认的字段分隔符是空格。
 $0:该变量包含当前记录的文本内容。
 $1:该变量包含第一个字段的文本内容。
 $2:该变量包含第二个字段的文本内容。
例如:

$ echo -e "line1 f2 f3\nline2 f4 f5\nline3 f6 f7" | \
awk '{
print "Line no:"NR",No of fields:"NF, "$0="$0,
"$1="$1,"$2="$2,"$3="$3
}'
Line no:1,No of fields:3 $0=line1 f2 f3 $1=line1 $2=f2 $3=f3
Line no:2,No of fields:3 $0=line2 f4 f5 $1=line2 $2=f4 $3=f5
Line no:3,No of fields:3 $0=line3 f6 f7 $1=line3 $2=f6 $3=f7

我们可以用print $NF打印一行中最后一个字段,用 $(NF-1)打印倒数第二个字段,其他字段以此类推。awk也支持printf()函数,其语法和C语言中的同名函数一样。
下面的命令会打印出每一行的第二和第三个字段:

$awk '{ print $3, $2 }' file

我们可以使用NR统计文件的行数:

$ awk 'END{ print NR }' file

5.5 将外部变量值传递给awk

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

$ VAR=10000
$ echo | awk -v VARIABLE=$VAR '{ print VARIABLE }'
10000

还有另一种灵活的方法可以将多个外部变量传递给awk。例如:

$ var1="Variable1" ; var2="Variable2"
$ echo | awk '{ print v1,v2 }' v1=$var1 v2=$var2
Variable1 Variable2

当输入来自于文件而非标准输入时,使用下列命令:

$ awk '{ print v1,v2 }' v1=$var1 v2=$var2 filename

在上面的方法中,变量以键-值对的形式给出,使用空格分隔(v1=var1v2=var1 v2=var2),作为awk的命令行参数紧随在BEGIN、{}和END语句块之后。

5.6 用getline读取行

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

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

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

5.7 使用过滤模式对awk处理的行进行过滤

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

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

5.8 设置字段分隔符

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

$ awk -F: '{ print $NF }' /etc/passwd

或者

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

5.9 从awk中读取命令输出

awk可以调用命令并读取输出。把命令放入引号中,然后利用管道将命令输出传入getline

"command" | getline output ;

下面的代码从/etc/passwd文件中读入一行,然后显示出用户登录名及其主目录。在BEGIN语句块中将字段分隔符设置为:,在主语句块中调用了grep。

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

5.10 awk的关联数组

除了数字和字符串类型的变量,awk还支持关联数组。关联数组是一种使用字符串作为索引的数组。你可以通过中括号中索引的形式来分辨出关联数组:

arrayName[index]

就像用户定义的简单变量一样,你也可以使用等号为数组元素赋值:

myarray[index]=value

5.11 在awk中使用循环

在awk中可以使用for循环,其格式与C语言中的差不多:

for(i=0;i<10;i++) { print $i ; }

另外awk还支持列表形式的for循环,也可以显示出数组的内容:

for(i in array) { print array[i]; }

下面的例子展示了如何将收集到的数据存入数组并显示出来。这个脚本从/etc/password中读取文本行,以:作为分隔符将行分割成字段,然后创建一个关联数组,数组的索引是登录ID,对应的值是用户名:

$ awk 'BEGIN {FS=":"} {nam[$1]=$5} END {for {i in nam} {print i,nam[i]}}' /etc/passwd
root root
ftp FTP User
userj Joe User

5.12 awk内建的字符串处理函数

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包含了匹配内容的长度。

六、统计特定文件中的词频

 1 #!/bin/sh
  2 
  3 if [ $# -ne 1 ]
  4 then
  5         echo "Usage: $0 filename";
  6         exit 1;
  7 fi
  8 
  9 filename=$1
 10 
 11 egrep -o "\b[[:alpha:]]+\b" $filename | \
 12 awk '{count[$0]++} 
 13 END{printf("%-14s\n","Word","Count");
 14         for(i in count)
 15         {
 16                 printf("%-14s%d\n",i,count[i]);
 17         }
 18 }'

egrep命令将文本文件转换成单词流,每行一个单词。模式\b[[:alpha:]]+\b能够匹配每个单词并去除空白字符和标点符号。选项-o打印出匹配到的单词,一行一个。

awk命令统计每个单词。它针对每一行文本执行{}语句块中的语句,因此我们不需要再专门为此写一个循环。count[$0]++命令负责计数,其中$0是当前行,count是关联数组。所有的行处理完毕后,END{}语句块打印出各个单词及其数量。

六、压缩或解压缩JavaScript

我们准备写一个JavaScript压缩工具,当然,还包括与之对应的解压缩工具。来考虑下面Javascript代码:

$ cat sample.js
function sign_out()
{
	$("#loading").show();
	$.get("log_in",{logout:"True"},
	
	function(){
		window.location="";
	});
}

6.1 压缩脚本

下面是压缩JavaScript代码所需要完成的工作
(1) 移除换行符和制表符。
(2) 移除重复的空格。
(3) 替换掉注释

$ cat sample.js | \
tr -d '\n\t' | tr -s ' ' \
| sed 's:/\*.*\*/::g' \
| sed 's/ \?\([{}();,:]\) \?/\1/g'

输出如下

function sign_out(){$("#loading").show();$.get("log_in",
{logout:"True"}, function(){window.location="";});}

(1) tr -d '\n\t’移除\n和\t
(2) tr -s ’ '移除多余空格
(3)sed 's:/*.**/::g’移除注释

因为我们需要使用/* 和*/,所以用:作为sed的分隔符,这样就不必对 / 进行转义了。
*在 sed 中被转义为\*。.*用来匹配/*与*/之间所有的文本。

(4) 移除{,}、(,)、;、:以及,前后的空格

sed 's/ \?\([{}();,:]\) \?/\1/g'

上面的sed语句含义如下。
 / ?([{}();,:]) ?/用于匹配,/\1/g 用于替换。
 ([{}();,:])用于匹配字符组[ { }( ) ; , : ](出于可读性方面的考虑,在这里加入了空格)中的任意一个字符。(和)是分组操作符,用于记忆所匹配的内容,以便在替换部分中进行向后引用。对(和)转义之后,它们便具备了另一种特殊的含义,可以作为分组操作符使用。位于分组操作符前后的?用来匹配可能出现在字符集合周围的空格。
 在命令的替换部分,匹配的字符串(也就是一个可选的空格、一个来自字符集的字符再加一个可选的空格)被匹配的子串所替换。由分组操作符匹配到并记忆的子串是通过向后引用来指代的。可以用符号\1向后引用该分组所匹配的内容。

6.2 解压脚本

$ cat obfuscated.txt | sed 's/;/;\n/g; s/{/{\n\n/g; s/}/\n\n}/g'

 s/;/;\n/g 将;替换为;\n;
 s/{/{\n\n/g 将{替换为{\n\n;
 s/}/\n\n}/g 将}替换为\n\n}。

©️2020 CSDN 皮肤主题: 书香水墨 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值