Shell编程之正则表达式与字符处理

misty 2019年06月11日 105次浏览

Shell编程之正则表达式与字符处理

正则表达式与通配符

正则表达式用来在文件中匹配符合条件的字符串,正则是包含匹配,grep,awk,sed等命令可以支持正则表达式

通配符用来匹配符合条件的文件名,通配符是完全匹配,ls |find| cp这些命令不支持正则表达式,所以只能用shell自己的通配符进行匹配了

注意这个概念只适合Linux系统

通配符

  • * 匹配任意内容

    • 例如:ls * 列出当前目录的所有文件,包括目录下的子文件
  • ? 匹配一个内容

    • ls misty?zhang 匹配以misty开头以zhang结尾中间可以是任意一个字符的一个字符串
    • ls misty??? 匹配以misty开头后面可以是任意三个字符的字符串
  • [] 匹配中括号中的一个字符

    • ls misty[io]zhang 匹配一个以misty开头zhang结尾,中间可以是括号中匹配的一个字符的字符串

在绝大多数的语言当中没有通配符,只有正则表达式,通配符只是正则表达式的符号概念实际还是正则表达式,但是在系统当中通配符用来匹配文件名的

正则表达式

正则表达式是用于描述字符排列和匹配模式的一种语法规则,主要用于字符串的模式分割,匹配,查找以及替换操作

基础正则表达式

img

  • grep "a*" test.txt 匹配*前的字符0次到任意多次,所以文档中的内容会全部匹配输出,当然匹配的部分会标红(如果grep 设置了如本文下的别名)

    • * 前面的一个字符是没有作用的代表匹配所有内容包括空白行
    • "aa*" 匹配至少包含有一个a的行
    • "aaa*" 匹配至少包含有两个连续个a的字符串
  • grep “aaaaa*” test.txt 匹配至少包含四个连续的a的字符串

  • . 匹配除了换行符之外的任意一个字符

    • "s..d"会匹配在s和d这两个字母之间一定有两个字符的字符串
    • "s..d" 会匹配s和d字母之间有任意字符
    • ".*" 匹配所有内容
  • "^" "$"  匹配行首和匹配行尾

    • "^M   匹配以大写M开头的行
    • "M$"  匹配以大写M结尾的行
    • "^$"  匹配空白行,grep -n "^$" test.txt 匹配空白行的行号
  • []匹配括号中指定的任意一个字符,只匹配一个字符

    • "s[ao]id":匹配a或者o中的任意一个
    • "[0-9]" 匹配任意一个数字
    • “[a-z]” 匹配任意一个小写字母

注意^的语法:在[]外部代表行首匹配,在[]内部代表取反^[^a-z] 匹配不用小写开头的行, ^[^a-zA-Z]匹配不用字母开头的行

  • 转义符\

    • \.$匹配使用 "." 结尾的行 ;如果不加转义符 .$代表匹配任何字符结尾的行(除了空行)

    • \{n\} 表示其前面的字符恰好出现n次

      • a\{3\}匹配a字母连续出现三次的字符串

      • [0-9]\{3\} 匹配包含连续的三个数字的字符串

      • \{n,\} 匹配其前面的字符出现不小于n次

      • ^[0-9]\{3,\}[a-z] 匹配最少用三个连续数字开头的行

      • \{n,m\}匹配其前面的字符至少出现n次最多m次

      • sa\{1,3\}i 在字母a和字母i之间至少有一个a至多有3个a

正则表达式案例

  • [0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\} 匹配日期格式 YYYY-MM-DD

  • [0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\ 匹配ip地址

  • 仅适用于从文档中间匹配正则,但是不适用于将ip写入文档

?和()是扩展正则;表达式语法和Java有点区别

扩展正则表达式

[省略]

字符截取命令

cut字段提取命令

cut [选项] 文件名

  • -f 列号:提取第几列

  • -d 分隔符:按照指定分隔符分割列(默认分割符号是制表符tab)

  • 实例:grep "/bin/bash" /etc/passwd |grep -v "root" |cut -f 1 -d ":" 获取用户排除root用户截取第一列的值就是普通用户

    • 实例:grep "/bin/bash" /etc/passwd |grep -v "root" |cut -f 1,3 -d ":" 获取用户排除root用户截取第一列和第三列的值就是普通用户用户名和uid

管道符操作不需要后接文件名,操作的结果是前一条命令传递的返回值;若是没有分隔符(当做分隔符在最后)则这一整行作为单元列

cut命令的局限

  • df -h |cut -d " " -f 1,3若是使用空格作为分隔符,处理起来会有问题,多个空格连在一起无法处理

df 命令用于查看分区的使用状况

printf命令-格式化输出命令

echo是原格式输出;printf不支持数据流操作也就不支持管道符;print的输出内容需要引号括起来

语法:printf '输出类型输出格式' 输出内容
输出类型

  • %ns:输出字符串,n是数字指代输出几个字符
  • %ni:输出整数,n是数字指代输出几个数字
  • %m.nf:输出浮点数,m,n都是数字,指代输出的整数位数和小数位数,如%8.2f代表共输出8位数,其中两位是小数,六位是整数

输出格式

  • \a:输出警告声音
  • \b:输出退格键也就是backpace键
  • \f:清除屏幕
  • \n:换行
  • \r:回车,也就是enter键
  • \t:水平输出退格键,也就是tab键
  • \v:垂直输出退格键,也就是tab键

操作实例

id  name    gender  mark
1   sange   F       60
2   lisi    F       70  
3   wangsu  F       80

printf $(cat student.txt) #这样输出有问题
printf '%s' $(cat student.txt) #不调整格式输出,这样输出没有任何格式,内容挤压在一起
printf '%s\t%s\t%s\t%s\n' $(cat student.txt) #格式化输出,每隔四个换一行
`</pre>

在awk命令的输出中支持print和printf命令
  • print:print会在给个输出之后自动加入一个换行符(linux默认没有print命令)

  • printf:printf是标准格式输出命令,并不会自动加入换行符,如果需要换行,需要手工加入换行符

    awk命令

    语法:
    awk '条件1{动作1}条件2{动作2}...' 文件名,有条件按照条件执行动作,没有条件直接执行动作

  • 条件(pattern)

    • 一般使用关系表达式作为条件
    • x>10判断变量 x是否大于10
    • x>=10 大于等于
    • x<=10小于等于
  • 动作(action)

    • 格式化输出
    • 流程控制语句

    操作实例

# 按行读出文档中的数据,再按条件截取(本例没有条件)第二个和第四个字段
 awk '{print[f] $2 "-t" $4 "\n" }' student.txt #没有条件直接执行动作,因为这里已经有单引号所以转义符用双引号,并且不可以省略
 # 仅仅截取根分区的磁盘占用情况,获取第五个参数,再去除%即可的到分区占用的比例
 df -h | grep "/dev/sda5" | awk `{print $5 } | cut -d "%" -f 1`

 # BEGIN 和 END 
 # 在所有的动作开始之前 执行一段输出BEGIN代表动作开始之前对应ENG(如果动作结束)
 awk 'BEGIN{printf "this is a transcript \n"}{printf $2 "\t" $4 "\n"}' student.txt

 ## FS内置变量
 #打印普通用户的第一列和第三列数据分隔符是`:`,导致第一行没有正确输出;
 # 因为awk是先读取一行然后赋予变量之后再执行动作,所以分隔符没有来得及指定
 cat /etc/passwd |grep /bin/bash |awk '{FS=":"}{print $1 "\t" $3}'
 # 用BEGIN解决这个问题,在所有的动作执行之前,执行指定分隔符而不是默认的分隔符
 cat /etc/passwd |grep /bin/bash |awk 'BEGIN{FS=":"}{print $1 "\t" $3}'

 # 关系运算符
 # 读取文本学生表排除表头,取出成绩大于70的学生打印出姓名,注意条件卸载大括号之外
 cat student.txt |grep -v Name |awk '$4>=70{print $2 "\n"}'
 ```

 #### sed命令-字符替换命令

 sed 是一种几乎包括所有Unix平台(包括Unix)的轻量级流编辑器,sed主要用来将数据进行选取,替换,删除,新增的命令

*   `sed [选项] '[动作]' 文件名`
*   选项

 *   `-n`:一般sed命令会把所有数据都输出到屏幕上,如果加入此选项只会吧经过处理的行输出到屏幕上
 *   `-e`:允许对输入数据应用多条sed命令编辑
 *   `-i`:用sed的修改结果直接修改读取数据的文件,而不是由屏幕输出
*   动作

     *   `-a`: 追加,在当前行后添加一行或者多行
 *   `-c`: 行替换,用c后面的字符串替换原数据行*   `-i`: 插入,在当前行前插入一行或者多行,
 *   `-d`: 删除,删除指定行*   `-p`: 打印输出指定的行*   `-s`: 字符替换,用一个字符串替换林一个字符串  ,格式为"行范围s/旧字串/新字串/g"(和vim的替换格式类似)

 **操作实例**

```shell
#查看文件的第二行,但是会把文本内容全部列出
 sed '2p' student.txt 
 #查看文件的第二行,并且输出到屏幕
 sed -n '2p' student.txt 
 # 在第二行后追加一段文本 append
 sed '2a  this is the text of test ' student.txt 
 # 在第二行前插入一行数据 insert
 sed '2i this is the text of test ' student.txt 
 # 整行替换:将第二行替换成另一个字符串
 sed '2c  this is the text of test' student.txt 

 #字符串部分替换
 sed 's/旧字串/新字串/g' 文件名
 #在第三行中将60换成99
 sed '3s/60/99/g' student.txt
 #在第三行中将60换成99,并且sed操作的数据直接写入文件
 sed -i '3s/60/99/g' student.txt
 # 同时把指定字符串替换成空,-e允许多条同时编辑
 sed -e 's/this is test1//g;s/this is test1//g' student.txt;
> 和vi有很多相似的地方,区别在于vim是给用户直接使用的,sed是为脚本或者程序提供文本处理支持

### 字符处理命令

sort命令-排序命令
  • sort [选项] 文件名

  • 选项:

    • -f : 忽略大小写
    • -n : 以数值型进行排序,默认使用字符串型进行排序
    • -r : 反向排序
    • -t : 指定分隔符,默认分隔符是制表符
    • -k [n,m] : 按照指定的字段范围进行排序,从第n字段开始,m字段结束(默认到行尾)

操作实例

    sort /etc/passwd # 排序用户信息文件,按照字母顺序
    sort -r /etc/passwd # 反向排序
    # 指定分隔符是":"  ,用第三字段开头,第三字段结尾排序,就是只用第三字段排序
    sort  -t ":" -k 3,3  # 会有个问题,会有数值小的在下面,是因为默认是字符串排序的

    # 指定分隔符是":" ,作为数值类型排序 ,用第三字段开头,第三字段结尾排序,就是只用第三字段排序
    sort -n -t ":" -k 3,3  
wc命令-统计命令
  • wc [选项] 文件名

  • 选项

    • -l:只统计行数
    • -w:只统计单词数,单词使用空格分割的
    • -m:只统计字符数
    # 大致统计用户数
    wc -l /etc/passwd 
    

  ###  其他相关命令和配置

  别名配置 `~/.bashrc`

  ```shell
  alias rm='rm -i'
  alias cp='cp -i'
  alias mv='mv -i'
  alias vi='vim'
  alias grep='grep --color=auto'

使配置文件生效source~/.basjrc