《Linux Shell脚本攻略》读书笔记第四章 让文本飞

1、正则表达式
正则表达式包括:文字字符,通配符,修饰符,锚点。
正则表达式字符类(POSIX):
表达式
字符类
ASCII中的对应域
[:alnum:]
字母数字混排
A-Za-z0-9
[:alpha:]
字母字符
A-Za-z
[:blank:]
空格符或者制表符
[:digit:]
数字
0-9
[:lower:]
小写字母
a-z
[:punct:] 可打印字符,不包括空格和字母数字混排字符
[:space:] 空白字符
[:upper:] 大写字符 A-Z
正则表达式语法总结:
字符
功能
正则表达式语法
解释
.
通配符
基本
代表一个任意字符
[abc],[a-z]
包含域
基本
代表域内任意一个字符
[^abc],[^a-z]
排除范围
基本
代表不包含在域内的任意一个字符
?
修饰符
扩展
代表0或者1个前面的项
*
修饰符
基本
代表0或者多个前面的项
+ 修饰符 扩展 代表1或者多个前面的项
{m,n} 修饰符 扩展 代表前面的项出现了m到n次之间
{n} 修饰符 扩展 代表前面的项具体出现的次数为n
^ 基本 标出一行的开始
$ 基本 标出一行的结束
\< 基本 标出一个单词的开始
\> 基本 标出一个单词的结束
(...) 分组 基本 允许修饰符修饰一组字符
(...|...) 分组 扩展 允许指定可选的模式
\ 转义 扩展(基本) 取消(或者启动)后续字符的特殊含义
元字符
正则表达式
描述
示例
\b
单词边界
\bcool\b匹配cool,不匹配cooler
\B
非单词边界
cool\B匹配cooler,不匹配cool
\d
单个数字字符
b\db匹配b2b,不匹配bcb
\D
单个非数字字符
b\Db匹配bcb,不匹配b2b
\w
单个单词字符(字母、数字与_)
\w匹配1或a,不匹配&
\W 单个非单词字符 \W匹配&,不匹配1或a
\s 单个空白字符 x\sx匹配x x,不匹配xx
\S 单个非空白字符 x\Sx匹配xkx,不匹配x x
\n 换行符 \n匹配一个新行
\r 回车 \r匹配回车
[root@stone ~]# egrep "( ?[a-zA-Z]+ ?)" num2
banana
cherry
grape
orange
[root@stone ~]# ifconfig | egrep "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"
          inet addr:172.16.3.54  Bcast:172.16.3.255  Mask:255.255.255.0
          inet addr:192.168.0.100  Bcast:192.168.0.255  Mask:255.255.255.0
          inet addr:127.0.0.1  Mask:255.0.0.0
          inet addr:192.168.122.1  Bcast:192.168.122.255  Mask:255.255.255.0

2、搜索文本 grep egrep
grep仅可以使用基本正则表达式,egrep才可以使用扩展正则表达式
grep -E 等同于egrep
grep 命令通用命令行选项
选项
作用
-c
仅打印出包含模式的行的数量
-h
禁用文件名前缀
-e 表达式
使用表达式作为搜索模式(有助于指定多个模式)
-i
在判断模式时否匹配时忽略大小写
-l
仅打印符合模式要求的文件名
-L 仅打印不符合模式要求的文件名
-n 包括行数在内的符合要求的行
-q “Quiet”。不要写任何标准输出,只要找到匹配行,返回值为0
-r 在目录中递归搜索所有文件
-w word 仅包含word的行
-A N
包括符合要求的那一行的后N行的内容
-B N 包括符合要求的那一行的前N行的内容
-C N 包括符合要求的那一行的前后N行的内容
-o 只打印匹配的文本
-f file 使用file中指定的模式进行匹配
--exclude file 不进行搜索的文件或目录,可用通配符匹配
--exclude-from file 不进行搜索file中指定的文件列表
-Z 输出nul字符,通常与-l配合使用
[root@stone ~]# ifconfig | egrep -o "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"
172.16.3.54
172.16.3.255
255.255.255.0
192.168.0.100
192.168.0.255
255.255.255.0
127.0.0.1
255.0.0.0
192.168.122.1
192.168.122.255
255.255.255.0
#仅打印匹配的文本

[root@stone ~]# ifconfig | egrep -o "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" | wc -l
11
#统计匹配的数量

[root@stone ~]# ifconfig | egrep "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" -c
4
#统计匹配的行数

[root@stone ~]# ifconfig | egrep "[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" -n
2:          inet addr:172.16.3.54  Bcast:172.16.3.255  Mask:255.255.255.0
11:          inet addr:192.168.0.100  Bcast:192.168.0.255  Mask:255.255.255.0
15:          inet addr:127.0.0.1  Mask:255.0.0.0
41:          inet addr:192.168.122.1  Bcast:192.168.122.255  Mask:255.255.255.0
#列出匹配的行的行号

[root@stone ~]# grep "grape" . -r -n      
./num.patch:8:+grape
./num1.orig:3:grape
./num2:3:grape
#递归搜索当前目录下所有含有”grape“的,并列出行号文件

[root@stone ~]# echo hello world | grep -i "HELLO"
hello world
#-i选项忽略大小写

[root@stone ~]# echo -e "apple\norange" | grep -e "apple" -e "orange"
apple
orange
#-e选项匹配多个模式

[root@stone ~]# echo -e "apple\norange" > pattenfiel
[root@stone ~]# cat num2
banana
cherry
grape
orange
[root@stone ~]# grep -f num2 pattenfiel 
orange
#-f选项指定模式文件

[root@stone ~]# grep "orange" num* -lZ
num1num1.orignum2num.patch
[root@stone ~]# grep "orange" num* -lZ | od -a
0000000   n   u   m   1 nul   n   u   m   1   .   o   r   i   g nul   n
0000020   u   m   2 nul   n   u   m   .   p   a   t   c   h nul
0000036
[root@stone ~]# grep "orange" num* -lZ | xargs -0 ls
num1  num1.orig  num2  num.patch
#-lZ选项输出nul字符,配合xargs -0 使用可规避含有空格的文件名

[root@stone ~]# seq 5 | grep 3 -A 2
3
4
5
#-A选项输出匹配行及后两行
[root@stone ~]# seq 5 | grep 3 -B 2
1
2
3
#-B选项输出匹配行及前两行
[root@stone ~]# seq 5 | grep 3 -C 2
1
2
3
4
5
#-C选项输出匹配行及前后两行

3、切分文件 cut
[root@stone ~]# cat student_data.txt 
No      Name    Mark    Percent
1       sarath  45      90
2       alex    49      98
3       anu     45      90
[root@stone ~]# cut -f1 student_data.txt 
No
1
2
3
#提取第一列
[root@stone ~]# cut -f2,4 student_data.txt 
Name    Percent
sarath  90
alex    98
anu     90
#提取第2,4列
[root@stone ~]# cut -f3 --complement student_data.txt 
No      Name    Percent
1       sarath  90
2       alex    98
3       anu     90
#提取除第3列外所有列
[root@stone ~]# cat student_data.txt 
student mark
No      Name    Mark    Percent
1       sarath  45      90
2       alex    49      98
3       anu     45      90
end
[root@stone ~]# cut -s -f1 student_data.txt 
No
1
2
3
#排除不含有定界符的行(默认定界符是tab)
[root@stone ~]# cat student_data.txt 
student mark
No,Name,Mark,Percent
1,sarath,45,90
2,alex,49,98
3,anu,45,90
end
[root@stone ~]# cut -s -f2 -d "," student_data.txt 
Name
sarath
alex
anu
#-d选项指定定界符

[root@stone ~]# echo 123456789 > rang_fields.txt
[root@stone ~]# cut -c1-5 rang_fields.txt 
12345
[root@stone ~]# cut -c-5 rang_fields.txt 
12345
[root@stone ~]# cut -c2-5 rang_fields.txt 
2345
[root@stone ~]# cut -c5- rang_fields.txt 
56789
#指定字段范围
[root@stone ~]# cut -c1-3,7-9 --output-delimiter="," rang_fields.txt 
123789
#--output-delimiter设置输出分隔符,但例子没有这个效果

4、统计词频
[root@stone ~]# egrep -o "\b[[:alpha:]]+\b" student_data.txt 
student
mark
No
Name
Mark
Percent
sarath
alex
anu
end
[root@stone ~]# egrep -o "\b[[:alpha:]]+\b" student_data.txt  | sort | uniq -c | sort -rn
      1 student
      1 sarath
      1 Percent
      1 No
      1 Name
      1 Mark
      1 mark
      1 end
      1 anu
      1 alex

5、sed入门
[root@stone ~]# cat student_data.txt 
student mark
No,Name,Mark,Percent
1,sarath,45,90
2,alex,49,98
3,anu,45,90
end
[root@stone ~]# sed 's/,/\t/' student_data.txt 
student mark
No      Name,Mark,Percent
1       sarath,45,90
2       alex,49,98
3       anu,45,90
end
#"s///"只替换每一行第一处匹配字符
[root@stone ~]# sed 's/,/\t/g' student_data.txt 
student mark
No      Name    Mark    Percent
1       sarath  45      90
2       alex    49      98
3       anu     45      90
end
#"s///g"替换所有匹配字符
[root@stone ~]# sed 's/,/\t/3g' student_data.txt 
student mark
No,Name,Mark    Percent
1,sarath,45     90
2,alex,49       98
3,anu,45        90
end
#"s///ng"从第n处开始替换所有匹配字符

[root@stone ~]# sed -i 's/,/\t/g' student_data.txt 
[root@stone ~]# cat student_data.txt 
student mark
No      Name    Mark    Percent
1       sarath  45      90
2       alex    49      98
3       anu     45      90
end
#-i选项表示将替换应用到原始文件

[root@stone ~]# cat student_data.txt 
student mark
No      Name    Mark    Percent
1       sarath  45      90
2       alex    49      98
3       anu     45      90

end
[root@stone ~]# sed '/^$/d' student_data.txt 
student mark
No      Name    Mark    Percent
1       sarath  45      90
2       alex    49      98
3       anu     45      90
end
#‘/^$/d'表示删除空白行

[root@stone ~]# sed 's/\w\+/[&]/g' student_data.txt 
[student] [mark]
[No]    [Name]  [Mark]  [Percent]
[1]     [sarath]        [45]    [90]
[2]     [alex]  [49]    [98]
[3]     [anu]   [45]    [90]

[end]
#&表示已经匹配的字符串,类似find和xargs中的{},\w\+匹配每一个单词

[root@stone ~]# echo this is digit 7 in a number | sed 's/digit \([0-9]\)/\1/'
this is 7 in a number
[root@stone ~]# echo first FIRST | sed 's/\([a-z]\+\) \([A-Z]\+\)/\2 \1/'
FIRST first
[root@stone ~]# echo first FIRST | sed 's/\(\w\+\) \(\w\+\)/\2 \1/'          
FIRST first
#\(pattern\)用于匹配子串,匹配的第一个字符串标记为\1,第二个标记为\2
[root@stone ~]# sed 's/\(\w\+\)/[\1]/g' student_data.txt 
[student] [mark]
[No]    [Name]  [Mark]  [Percent]
[1]     [sarath]        [45]    [90]
[2]     [alex]  [49]    [98]
[3]     [anu]   [45]    [90]

[end]

[root@stone ~]# text=hello
[root@stone ~]# echo hello world | sed "s/$text/HELLO/"
HELLO world
#档表达式中有变量时,需要使用双引号

6、awk入门
工作原理
[root@stone ~]# echo -e "line1\nline2" | awk 'BEGIN{print "start"}{print}END{print "End"}'
start
line1
line2
End
#print不带参数时会打印当前行
[root@stone ~]# echo | awk '{var1="v1";var2="v2";var3="v3";print var1,var2,var3}' 
v1 v2 v3
#print的参数以“,”(逗号)分割时,参数打印时则以空格为分界符
#awk中对变量赋值时,变量的值需要使用双引号括起来
#awk中对变量的引用,不需要使用$
[root@stone ~]# echo | awk '{var1="v1";var2="v2";var3="v3";print var1"-"var2"-"var3}'
v1-v2-v3
#在awk的print语句中,如果需要使用拼接符,则需要用双引号括起来
[root@stone ~]# echo | awk '{var1="v1";var2="v2";var3="v3";print var1-var2-var3}'    
0

特殊变量
NR:表示记录数量(number of records),在执行过程中对应与当前行号
NF:表示字段数量(number of fields),在执行过程中对应于当前行的字段总数
$0:这个变量包含执行过程中当前行的文本内容
$1:这个变量包含第一个字段的文本内容
$2:这个变量包含第二个字段的文本内容
[root@stone ~]# cat rang_fields.txt 
line1 f1 f2
line2 f3 f4
line3 f5 f6
[root@stone ~]# cat rang_fields.txt | awk '{print "line_no:"NR,"field_no:"NF,"$0="$0,"$1="$1,"$2="$2,"$3="$3}'
line_no:1 field_no:3 $0=line1 f1 f2 $1=line1 $2=f1 $3=f2
line_no:2 field_no:3 $0=line2 f3 f4 $1=line2 $2=f3 $3=f4
line_no:3 field_no:3 $0=line3 f5 f6 $1=line3 $2=f5 $3=f6

[root@stone ~]# cat rang_fields.txt | awk '{print $NF,$(NF-1)}'              
f2 f1
f4 f3
f6 f5
#print $NF打印一行中最后一个字段
#print $(NF-1)打印一行中倒数第二个字段

[root@stone ~]# awk '{print $3,$2}' rang_fields.txt 
f2 f1
f4 f3
f6 f5

[root@stone ~]# awk 'END{print NR}' rang_fields.txt 
3
#统计文件的行数,实际上是打印最后一行的行号
[root@stone ~]# awk '{print NR}' rang_fields.txt    
1
2
3


[root@stone ~]# seq 5 | awk 'BEGIN{sum=0;print "summation:"}{print $1"+";sum+=$1}END{print "==";print sum}'
summation:
1+
2+
3+
4+
5+
==
15

外部变量传递
[root@stone ~]# var=10000
[root@stone ~]# echo | awk -v var1=$var '{print var1}'
10000
#-v选项可以将外部值传递给awk
[root@stone ~]# var1=10;var2=20                             
[root@stone ~]# echo | awk '{print v1,v2}' v1=$var1 v2=$var2
10 20

[root@stone ~]# awk '{print v1,v2}' v1=$var1 v2=$var2 rang_fields.txt 
10 20
10 20
10 20
#变量之间以空格分隔,在BEGIN,{}和END语句块之后

getline读取行
[root@stone ~]# seq 5 | awk 'BEGIN{getline;print "aaa",$0}{print $0}'                     
aaa 1
2
3
4
5
[root@stone ~]# seq 5 | awk 'BEGIN{print "aaa",$0}{print $0}'        
aaa 
1
2
3
4
5

行过滤
[root@stone ~]# seq 5 | awk 'NR<=3{print $0}'
1
2
3
#读取行号小于等于3的行
[root@stone ~]# seq 5 | awk 'NR==3{print $0}' 
3
#读取行号等于3的行
[root@stone ~]# seq 5 | awk '/3/{print $0}'     
3
#读取内容包含3的行
[root@stone ~]# seq 5 | awk '!/3/{print $0}' 
1
2
4
5
#读取内容不包括3的行

设置定界符
[root@stone ~]# head -5 /etc/passwd | awk -F ":" '{print $NF}'
/bin/bash
/sbin/nologin
/sbin/nologin
/sbin/nologin
/sbin/nologin
[root@stone ~]# head -5 /etc/passwd | awk 'BEGIN{FS=":"}{print $NF}'        
/bin/bash
/sbin/nologin
/sbin/nologin
/sbin/nologin
/sbin/nologin

读取命令输出
[root@stone ~]# echo | awk '{"grep root /etc/passwd"|getline cmdout;print cmdout}'
root:x:0:0:root:/root:/bin/bash
#将命令的输出读入变量output的语法为:“command”|getline output

在awk中使用循环
[root@stone ~]# cat rang_fields.txt 
line1 f1 f2
line2 f3 f4
line3 f5 f6
[root@stone ~]# awk '{for(i=1;i<=NF;i++) {print $i}}' rang_fields.txt    
line1
f1
f2
line2
f3
f4
line3
f5
f6

awk内建字符串控制函数


7、文件行,单词,字符选取
选取每一行
重定向方式:
[root@stone ~]# while read line; do echo $line; done < rang_fields.txt                  
line1 f1 f2
line2 f3 f4
line3 f5 f6
子shell方式:
[root@stone ~]# cat rang_fields.txt | ( while read line;do echo $line;done )
line1 f1 f2
line2 f3 f4
line3 f5 f6

选取每一个单词
[root@stone ~]# while read line;
> do
> for word in $line;
> do
> echo $word;
> done
> done < rang_fields.txt 
line1
f1
f2
line2
f3
f4
line3
f5
f6

选取每一个字符
[root@stone ~]# word=abcdefg
[root@stone ~]# for((i=0;i<${#word};i++)); do echo ${word:i:1}; done
a
b
c
d
e
f
g

8、按列合并文件 paste
[root@stone ~]# cat num1
apple
banana
mango
orange
peach
pear
strawberry
watermelon
[root@stone ~]# cat num2
banana
cherry
grape
orange
[root@stone ~]# paste num1 num2
apple   banana
banana  cherry
mango   grape
orange  orange
peach
pear
strawberry
watermelon
[root@stone ~]# paste num1 num2 -d ":"
apple:banana
banana:cherry
mango:grape
orange:orange
peach:
pear:
strawberry:
watermelon:
#-d选项指定分隔符,默认是制表符

9、打印文件第n列
[root@stone ~]# cat rang_fields.txt 
line1 f1 f2
line2 f3 f4
line3 f5 f6
[root@stone ~]# awk '{print $3}' rang_fields.txt 
f2
f4
f6
[root@stone ~]# cut -d " " -f 3 rang_fields.txt 
f2
f4
f6

10、打印文件某些行
[root@stone ~]# cat num1
apple
banana
mango
orange
peach
pear
strawberry
watermelon
[root@stone ~]# awk 'NR==3,NR==5' num1
mango
orange
peach
[root@stone ~]# seq 9 | awk 'NR==4,NR==6'
4
5
6

[root@stone ~]# awk '/ba.*a/,/^p/' num1     
banana
mango
orange
peach
[root@stone ~]# awk '/ba.*a/,/h$/' num1  
banana
mango
orange
peach

11、检查回文字符串
[root@stone ~]# echo -e "aa\nbc"|sed -n '/\(.\)\1/p'
aa
[root@stone ~]# cat bin/match_palindrome.sh 
#!/bin/bash
if [ $# -ne 2 ];then
echo "usage: $0 filename string_length"
exit -1
fi

filename=$1
basepattern='/^\(.\)'
#开头一个字符
count=$(($2/2))
#字符数一半

for((i=1;i<$count;i++))
do
basepattern=$basepattern'\(.\)';
done
#设定匹配模式

if [ $(($2%2)) -ne 0 ];then
basepattern=$basepattern'.';
fi
#考虑字符总数为奇数的情况

for((count;count>0;count--))
do
basepattern=$basepattern'\'"$count";
done
#反向引用并拼接

basepattern=$basepattern'/p'
sed -n "$basepattern" $filename

[root@stone ~]# match_palindrome.sh num2 5
stats
napan
[root@stone ~]# cat num2
banana
cherry
grape
orange
peep
noon
stats
napan
oppo
[root@stone ~]# match_palindrome.sh num2 4
peep
noon
oppo
[root@stone ~]# match_palindrome.sh num2 5
stats
napan

rev命令
[root@stone ~]# string="malayalam"
[root@stone ~]# if [ "$string" == "$(echo $string | rev )" ];then echo "palindrome" ;else echo "not palindrome" ;fi
palindrome
[root@stone ~]# sentence='i love my country' 
[root@stone ~]# echo $sentence | rev | tr ' ' '\n' | tac | tr '\n' ' ' | rev
 country my love i

12、以逆序形式打印
[root@stone ~]# seq 5 | tac
5
4
3
2
1
[root@stone ~]# seq 5 | awk '{lifo[NR]=$0;lno=NR}END{for(;lno>-1;lno--){print lifo[lno];}}'
5
4
3
2
1

13、解析email地址和URL
[root@stone ~]# man echo | egrep -o '[A-Za-z0-9.]+@[A-Za-z0-9.]+\.[a-zA-Z]{2,4}'  
[root@stone ~]# man echo | egrep -o 'http://[A-Za-z0-9.]+\.[a-zA-Z]{2,4}'              
http://www.gnu.org

14、移除包含特定单词的句子
[root@stone ~]# cat regular_express.txt
"Open Source" is a good mechanism to develop programs.
apple is my favorite food.
# I am VBird
[root@stone ~]# cat regular_express.txt | sed 's/[^.]*food[^.]*\.//g'
"Open Source" is a good mechanism to develop programs.

# I am VBird

15、替换变量内容与变量子串
[root@stone ~]# var="this is a line of text"
[root@stone ~]# echo ${var/line/word}
this is a word of text
#使用word替换line

[root@stone ~]# seq=1234567890
[root@stone ~]# echo ${seq:4}
567890
#获取第4个字符之后的所有字符

[root@stone ~]# echo ${seq:4:2}
56
#获取第4个字符之后的2个字符
[root@stone ~]# echo ${seq:(-1)}
0
#获取倒数第1 个字符

[root@stone ~]# echo ${seq:(-3):2}
89
#获取倒数第3个字符后的两个字符

你可能感兴趣的:(linux,shell,让文本飞)