0%

Shell实用工具

Regular Expressions

许多 Unix commands 能够识别正则表达式,提供了一种匹配模式。比如使用正则表达式进行过滤,有些命令是不能使用正则表达式的,比如 ls,但是可以接受通配符 *, ?, []

书中以 ed 为例演示正则表达式。

1,$s/old/new/g 1,$ 从第一行到最后一行,s 表示进行替换,old 表示要替换的内容,可以是正则表达式匹配,new是新的内容,g 表示全局替换。

匹配任意单字符 The Period .

正则表达式中的 . 号匹配 any single character,无论是什么都能匹配。

比如 r. 匹配 r 后跟任何单个字符。.x. 匹配由任意两个字符包围的 x,不一定需要相同。

匹配开头 插入符 The Caret ^

如果插入符是在一个正则表达式的开头,会匹配行的开头,^George

  • 在每一行插入 >> 符号,1,$s/^/>>/
  • 找到以 the 开头的行,/^the/

匹配结尾 $

匹配结尾,contents$ 匹配以 contents 结尾的行,那么 .$ 匹配什么呢?是以 . 号结尾的行吗,并不是,而是会匹配任何单个字符结尾的行,包括 .

那么要怎样匹配 . 呢,如果想匹配任何在正则表达式中有特殊意义的字符,使用 \ backslash 覆盖特殊意义。\.$ 会匹配以 . 结尾的行。^\. 匹配以 . 开头的行。

1,$s/$/>>/ 为每行末尾添加 >>1,$s/..$// 去除每行末尾两个字符。 ^$ 匹配空行, 空行意味着没有任何字符的行,而 ^ $ 则匹配只有一个空格的行,不包含其他任何字符。

使用 [] 构造器匹配字符集

[chars] 匹配这些字符中的一个,也可以用 - 指定范围。[0-9]匹配一个数字。 [tT]he 匹配 t 和 T 之后立即跟随 he,也就是 theThe

1,$s/[aeiouAEIOU]//g 删除所以元音字符。[0123456789] == [0-9]/[0-9]/ 找包含数字的行,^[A-Z] 以大写字母开头的行,1,$s/[A-Z]/*/g 把所有大写字母替换成 *

* 号也有特殊含义,但是在 ed 中,只有在匹配字符串中,特殊符号才有对应的意义,在替换字符串中没有特殊意义。

如果 ^ 出现在 [ 之后,则表示取反。[^A-Z]匹配除大写字母外的所有字符,[^A-Za-z] 匹配任何非字母字符,1,$s/[^A-Za-z]//g,删除所有非字母字符。

使用 * 匹配任意多个字符

shell 使用 Asterisk 来进行 filename substitution,会匹配 0 或者 多个字符。形成正则表达式时,星号用于匹配正则表达式的前一个元素零次或多次出现。

比如 X* 匹配 0、1、2… 个大写 X,而 XX* 则匹配一个或多个 X,因为表达式中指定了一个 X 后跟随 0 个或多个 X,可以使用 + 号完成同样的事,+ 匹配一个或多个,至少出现一次,所以 XX*X+ 是等价的。

1,$s/ */ /g 把所有多余的空格去掉,This is an example —> This is an example,这个表达式告诉 ed 把所有一个空格后跟 0 或多个空格的字符串替换成一个空格。

.* 通常用来匹配0多个任意字符。Bear in mind that a regular expression matches the longest string of characters that match the pattern. 最长匹配原则,所以这个表达式总是匹配整行。

e.*e 匹配一行中从第一个 e 到最后一个 e 的所有字符。elementvim 中使用 :%s/e.*e/a/gc 替换为 ant,最长匹配。

怎么匹配 -] 呢,[-0-9] 或者 [0-9-][]a-z]

匹配精确数量的字符 \{ ...\}

XX* 匹配 X 后跟随 0 或多个 XXXX* 匹配至少两个 XX 后跟随 0 或多个 X,有更方便的方式 \{min, max\} 完成这个任务,min 表示最少匹配多少次前面的正则表达式的模式,max 表示最多匹配多少次,必须使用转义字符转义。还有种特殊模式 \{10\} 表示前面的正则模式必须匹配 10 次,才算匹配上,如果只有一个数字 \{1,\}则表示至少出现一次到最长的那次,+\{5,\} 匹配至少五个加号,如果超过五个,匹配最长的那个。

[A-Za-z]\{4,7\} 匹配长度为 4 到 7 的字母串。1,$s/^.\{10\}// 删除每行开头的 10 个字符,如果不足 10 就不会删除,如果硬要删呢,使用 1,$s/^.\{0,10\}//

1,$s/.\{5\}$// 删除每行最后 5 个字符。

1,$s/[a-zA-Z]\{6,\}/X/g 把长度为 6 和以上的单词替换成 X

保存匹配到的字符 (…)

通过将这些字符括在反斜杠括号内,可以引用与正则表达式匹配的字符。这些被捕获的字符会存在正则表达式解析器预定义的 register 中,一共九个,1 到 9。

比如 ^\(.\) 匹配行上的第一个字符并存到 register 1 中。可以使用 \n 来引用存在 register 中的内容 n >= 1 and n <= 9

比如 ^\(.\)\1 匹配行上的第一个字符,并把它存到 register 1 中,然后匹配存在 register 1 中的字符,最终效果是行上开头两个字符相等就匹配。

比如 ^\(.\).*\1$ 匹配每行开头字符和结尾字符相等的行。

比如 ^\(...\)\(...\)\2\1 匹配 12 个字符,其中 1-3 个字符和 10-12 相等,4-6 和 7-9 相等。

:%s/\([a-z]*\) *\([a-z0-9]*\)/\2 \1/gc 交换两列。

正则表达式太神奇了。

总结

符号 含义 example 匹配
. Any character a.. a后面跟两个任意字符
^ Beginning of line ^wood wood开头的行
$ End of line x$ 最后一个字符是 x
^INSERT$ 只包含 INSERT 的行
^$ 空行
* 前面的正则出现 0 或多次 X*
XX*
0 或多个 X
1 或 多个 x
+ 前面的正则出现 1 或多次 X+
XX+
1 或多个
2 或多个
[chars] 匹配列表中任意一个 [tT]
[a-z]
大小写 t
小写字母中的一个
[^chars] 不匹配列表中的任意一个 [^0-9] 非数字
\{min, max\} 前面的正则至少和之多出现几次 x\{1,5\} 至少1个 x 或 至多 5 个 x
\(...\) 保存匹配到的字符到注册器上 ^\(.\) 第一个字符保存到 register 1

cut

paste

连接不同文件中对应的行。

paste -d "ab" file1 file2 file3 连接符号用来分隔不同部分,a 连接 file1file2b 连接 file2file3。如果只指定一个,那么会应用到多个文件中。

最好用单引号包裹连接符。

-s 选项对单个文件操作,把单个文件中的每一行用连接符连接起来。paste -s -d '+' numbers

sed

sed is stream ed,a program used for editing data in a pipe or command sequence.

尽管命令和 ed 相似,但不同的是,它不能交互式使用,通用格式 sed command filecommanded 风格的,作用于文件中的每一行,如果未指定文件,则从 STD IN 读入。

sed 's/Unix/UNIX' intro,养成使用单引号包裹命令的习惯。 把文件中每行第一个出现的 Unix 替换为大写,如果要把行中所有出现的匹配都替换掉要使用全局模式(g)。

who | sed 's/ .*$//' 提取用户名。

-n Option

默认情况下 sed 会把输入的每一行都输出到标准输出,不管行是否发生了变化。有时,只想从文件中提出某些行。

sed -n '1,2p' intro,只打印前两行,sed -n '/UNIX/p' intro,打印包含 UNIX 的行。

deleting lines

sed '1,2d' intro 删除前两行。sed /UNIX/d intro 删除包含 UNIX 的行,其实并不是删除,是过滤掉这些行。

tr

translate or delete characters,tr from-chars to-charsfrom-charsto-chars 是一个或多个或者一些字符的集合,任何出现在 from-chars 中的字符都会被变成对应 to-chars 位置上的字符。

tr e x < introtr 的输入必须要从文件中重定向,因为 tr 是从标准输入读取的,结果输出到标准输出,且源文件不变。

cut -d: -f1,6 /etc/passwd | tr : '\t',如果想使用不可打印字符,可以在 tr 中使用八进制表示。

date | tr ' ' '\12'

tr能转换一系列的字符,tr '[a-z]' '[A-Z]' < intro 把文件中所有小写字符都替换为大写字符。

tr '[a-zA-Z]' '[A-Za-z]' 大写变小写,小写变大写。

-s Option

使用 -s 可以去掉连续多次出现在 to-chars 中的字符,如果用 to-chars 替换后的连续多次重复出现,则使用单个字符替换。

tr -s ':' '\11' 把所有 colons 转变成 tab,使用单个 tab 替换多个,所以输入中一次或多次出现的 colons 会被一个 tab 替代。

this is goodtr -s ' ' ' ' < lotsaspaces

-d Option

tr -d from-chars 删除 from-chars 中的字符。 tr -d ' ' < intro 删除所有空格。

同样可以使用 sed 's/ //g' intro 来达到相同的效果,解决方式很多,但是这里 tr 更好点,它更小更快。

tr '()' '{}' 把左括号变成左花括号,右括号变成右花括号。

grep

grep 允许你搜索一个或多个文件来查找指定内容。grep pattern files

每个文件的每一行只要包含来匹配模式都会被输出到标准输出。

grep * filegrep '*' file 是不同的,grep -i 'the' intro-i 忽略大小写。

grep 可以接收正则表达式

-v Option

grep -v 'UNIX' intro ,不包含 UNIX 的行。

-l Option

有时希望找到哪些文件包含特定的字符,而不对具体行感兴趣。

grep -l 'Move_history' *.c 找到所有包含 Move_history 的文件。

-n Option

grep -n 'Move_history' testch.c 打印匹配到的行的行号。

sort

sort names 默认升序排序。

-u 去掉重复的行,早期的 Unix 使用者通常使用另外一个工具实现相同的效果 sort | uniq

-r 反向排序。 -n 把行当作数值处理在排序。 -k2n k == skip,跳过某个字段,-k2n 意味着从以第二列为排序列,默认使用 \t 分割行,可以使用 -t 指定分隔符。

sort -k3n -t: /etc/passwd sort by user id。

uniq

uniq in_file out_file,把输入文件中有重复的行去掉,在输出到第二个文件。uniq 只会找相连的行来判断是否重复,所以一般要和 sort 一起使用。

sort names | uniq

-d 找到重复的行,sort names | uniq -d 列出重复的行。

sort /etc/passwd | cut -f1 -d: | uniq -d 找到重复的用户。

-c 列出重复出现的次数。

tr '[A-Z]' '[a-z]' datafile | sort | uniq -c | head 找出数据文件中出现最多的单词。