2-shell-正则表达式

71 阅读9分钟

Linux Shell 脚本

shell基础正则表达式

正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑。并规定一些特殊语法表示字符类、数量限定符和位置关系,然后用这些特殊语法和普通字符一起表示一个模式,这就是正则表达式( Regular Expression )

元字符

  • 定位符(锚点) --> 匹配文本中的特定位置

    元字符功能示例匹配结果说明
    ^匹配行首^Hello匹配以"Hello"开头的行
    $匹配行尾world$匹配以"world"结尾的行
    \b单词边界\bcat\b匹配独立单词"cat"
    \B非单词边界\Bcat\B匹配"scatter"中的"cat"
  • 字符类 --> 匹配特定字符集合

    元字符功能示例匹配结果说明
    .匹配任意单个字符a.c"abc", "adc", "a c"等
    [...]匹配括号内任意字符[aeiou]任意一个元音字母
    [^...]匹配不在括号内的字符[^0-9]任意非数字字符
    \w匹配字母/数字/下划线\w+"Ab1", "word_" 等连续字符
    \W匹配非字母/数字/下划线\W"@", " ", "-" 等符号
    \d匹配数字\d{3}"123", "007" 等三位数字
    \D匹配非数字\D+"abc", "#$%" 等非数字串
    \s匹配空白符(空格/Tab等)\s+连续的空白区域
    \S匹配非空白符\S"A", "1", "@" 等可见字符
  • 重复限定符 --> 控制匹配次数

    元字符功能示例匹配结果说明
    *0次或多次重复ab*c"ac", "abc", "abbc"
    +1次或多次重复ab+c"abc", "abbc" (排除"ac")
    ?0次或1次重复colou?r"color" 或 "colour"
    {n}精确匹配n次\d{4}四位数字如"2023"
    {n,}至少匹配n次a{2,}"aa", "aaa" 等
    {n,m}匹配n到m次o{2,4}"oo", "ooo", "oooo"
  • 分组与捕获 --> 组合子表达式或捕获内容

    元字符功能示例匹配结果说明
    (...)捕获分组(ab)+"ab", "abab" (捕获为$1)
    (?:...)非捕获分组`(?:ab)+cd`匹配"abab"或"cd"但不捕获
    ``逻辑或`catdog`"cat" 或 "dog"
    \1,\2...反向引用已捕获分组(\w+)\s+\1匹配重复单词如"the the"
  • 特殊字符

    元字符功能示例匹配结果说明
    ``转义特殊字符.匹配字面点号"."
    -字符范围(括号内生效)[a-z]任意小写字母
    ^取反(括号内首位生效)[^A-Z]非大写字母字符

替换符

  • 变量基本扩展(Variable Expansion)

    功能:获取变量值并进行基础处理,是Shell脚本中最常用的扩展类型。

    操作符作用描述示例
    ${var}获取变量var的值(基础形式,通常可省略大括号,如$varname="Alice"; echo "Hello ${name}"Hello Alice
    ${var:-default}var未定义或为空,返回default;否则返回var的值(默认值替换)age=""; echo ${age:-18}18age为空,使用默认值18)
    ${var:=default}var未定义或为空,将var赋值为default并返回;否则返回var的值unset score; echo ${score:=0}; echo $score0(赋值并返回)
    ${var:?error}var未定义或为空,输出error并终止脚本;否则返回var的值(错误检查)unset id; ${id:?id is required} → 报错:-bash: id: id is required
    ${var:+value}var已定义且非空,返回value;否则返回空(条件替换)status=ok; echo ${status:+success}success(条件满足返回值)
    #!/usr/bin/env bash
    #
    read -p "input: " sss1
    sss1=${sss1:-1}
    echo $sss1                  <--- 不输入任何东西返回 sss1
    ​
    read -p "input: " sss2
    sss2=${sss2:=2}
    echo $sss2                  <--- 不输入任何东西返回 sss2
    ​
    read -p "input: " sss3
    sss3=${sss3:?3}
    echo $sss3                  <--- 不输入任何东西 异常退出脚本
    ​
    read -p "input: " sss4
    sss4=${sss4:+4}
    echo $sss4                  <--- 不输入任何东西返回 sss4, 只能是数字才+1,字母也是4
    
  • 字符串截取与替换(String Manipulation)

    功能:对变量值进行截取、替换或删除部分内容,适用于处理路径、文件名等场景。

    操作符作用描述示例
    ${var:position}position(0开始)截取子串至末尾(正向截取)path="/home/user/file.txt"; echo ${path:5}/user/file.txt (从第5位截取)
    ${var:position:length}position截取长度为length的子串str="hello"; echo ${str:1:3}ell(从第1位截取3个字符)
    ${var/pattern/replace}替换第一个匹配pattern的子串为replace(单次替换)text="apple banana apple"; echo ${text/apple/orange}orange banana apple
    ${var//pattern/replace}替换所有匹配pattern的子串为replace(全局替换)text="apple banana apple"; echo ${text//apple/orange}orange banana orange
    ${var/#pattern/replace}仅替换开头匹配pattern的子串为replace(前缀替换)str="hello world"; echo ${str/#hello/hi}hi world(替换开头)
    ${var/%pattern/replace}仅替换末尾匹配pattern的子串为replace(后缀替换)str="hello world"; echo ${str/%world/there}hello there(替换末尾)
  • 模式匹配与通配符

    功能:通过通配符(*?[]等)匹配符合规则的字符串,常用于文件名匹配或条件判断。

    通配符/操作作用描述示例
    *匹配任意长度的任意字符(包括空字符)ls *.txt → 列出当前目录所有.txt文件
    ?匹配单个任意字符ls file?.log → 匹配file1.logfileA.log (单个字符占位)
    [abc]匹配abc中的任意一个字符(字符集)ls [xy]* → 匹配以xy开头的文件
    [!abc][^abc]匹配除abc外的任意字符(取反字符集)ls [!0-9]* → 匹配不以数字开头的文件
    {a,b,c}匹配abc中的任意一个字符串(大括号展开,非通配符,预先生成候选)echo {1,2,3}.txt1.txt 2.txt 3.txt (生成多个字符串)
    [[ $var == pattern ]]在条件判断中使用通配符匹配变量值(需配合[[ ]]关键字)name="test.txt"; if [[ $name == *.txt ]]; then echo "txt file"; fitxt file(匹配成功)
  • 示例

    ]# he="hello world"
    ]# he2="hello world hello world"# ${#}  左边最短匹配模式   # 如果不加*  从顶头开始
    ]# echo ${he#*o}   # 结果为:world
    ]# echo ${he2#*o}  # 结果为: world hello world# ${##}  左边最长匹配模式
    ]# echo ${he##*o}  #  结果: rld
    ]# echo ${he2##*o} #  结果: rld   匹配最左边最长的字符串# ${%}   右边最短匹配模式  注意匹配 * 的位置
    ]# echo ${he%o*}   #  结果: hello w
    ]# echo ${he2%o*}  #  结果: hello world hello w# ${%%}  右边最长匹配模式
    ]# echo ${he%%o*}  #  结果: hell
    ]# echo ${he2%%o*} #  结果: hell# ${/ /}  替换左起的第一个符合的字符串
    ]# echo ${he2/world/xiong}       结果: hello xiong hello world# ${// /}  替换所有符合的字符串, 按最深度匹配
    ]# echo ${he2//world/xiong}     结果: hello xiong hello xiong
    

运算符

  • 逻辑运算符

    符号作用
    && 或 -a如果前值为true,则执行
    或 -o如果前值为false,则执行
    !exp
  • 文件运算符

    符号作用
    -e file如果给定的文件存在,则条件测试的结果为真。
    -(rwx) file如果给定的文件存在,且是可( 读, 可写,可执行的) 则测试结果为真
    -s file如果给定的文件存在,且其大小大于0
    -S file文件存在且是套接字(socket)
    -f file如果给定的文件存在,且是一个普通文件
    -d file如果给定的文件存在,且是一个目录
    -L file如果给定的文件存在,且是一个符号链接文件
    -c file如果给定的文件存在,且是字符特殊文件
    -b file如果给定的文件存在,且是块特殊文件
    -p file如果给定的文件存在,且是命名的管道文件
    ]# cat test.sh 
    #!/bin/bash
    #
    # 判断是否是套接字
    [ -S '/run/docker.sock' ] && echo '是一个套接字'
    # 判断是否为目录,如果不存在则创建
    [ -d './dir' ] && echo '是一个目录' || mkdir ./dir
    # 判断是否为文件,如果不存在则touch
    [ -f './file' ] && echo '是一个文件' || touch ./file
    # 判断是否为符号链接
    [ -L './link' ] && echo "是一个软链接" 
    
  • 字符串运算符

    符号作用示例
    =检测两个字符串是否相等,相等返回 true。[ a=a = b ] 返回 false
    !=检测两个字符串是否相等,不相等返回 true。[ a!=a != b ] 返回 true
    -z检测字符串长度是否为0,为0返回 true。[ -z $a ] 返回 false
    -n检测字符串长度是否为0,不为0返回 true。[ -n $a ] 返回 true
    str检测字符串是否为空,不为空返回 true。[ $a ] 返回 true
    =~正则表达式匹配运算符,用于匹配正则表达式的,配合[[]]使用if [[ ! $file =~ check$ ]],用于判断 $file 是否是以 check 结尾
    ]# cat test.sh 
    #!/bin/bash
    #
    value1="xiong"
    value2="xion222"# 判断两个值是否相等,
    [ $value1 = $value2 ] && echo 'yep' || echo '不相等'# 获取docker的pid值,然后在获取docker.sock判断docker是否挂掉,如果都为空说明挂了
    pid=`cat /run/docker.pid`
    ​
    # 判断pid是否为空, 如果为空为true
    [ -z '$pid' ] && echo '字符串为空' || echo '字符串有值 ' $pid# 使用-n会更好判断, 如果不为空为true
    [ -n '$pid' ] && echo $pid || echo '字符串为空'# 判断字符串是否为空,如果非空为true
    [ $value2 ] && echo '不为空'  || echo '为空'
    
  • 关系运算符

    符号作用示例
    -eq检测两个数是否相等,相等返回 true。[ aeqa -eq b ] 返回 true
    -ne检测两个数是否相等,不相等返回 true。[ anea -ne b ] 返回 true
    -gt检测左边的数是否大于右边的,如果是,则返回 true。[ agta -gt b ] 返回 false
    -lt检测左边的数是否小于右边的,如果是,则返回 true。[ alta -lt b ] 返回 true
    -ge检测左边的数是否大等于右边的,如果是,则返回 true。[ agea -ge b ] 返回 false
    -le检测左边的数是否小于等于右边的,如果是,则返回 true。[ alea -le b ] 返回 true
    []一对方括号,用于判断条件是否成立[ a==a == b ],注意添加 4 个空格
    [[]]两对方括号是对[]的扩展,可使用<、>、&&、等运算符[[ a>a>b ]],只需要添加左右两边两个空格, 需要注意:使用==与!=时,仍需要4个空格
    • 说明

      • 运算符 [] 与 [[]] 的区别

        实际上是 Bash 中 test 命令的简写,即所有的 [ expression ] 等于 test expression。而[[ expression ]]是 Bash 中真正的条件判断语句,其语法更符合编程习惯,建议使用。

      • Shell 中没有 <= 与 >= 运算符,只能使用 -le 与 -ge 替代。

    • 示例

      num=100
      num2=91
      ​
      # 两值相等为true           不相等
      [ $num -eq $num2 ] && echo '相等' || echo '不相等'
       
      # 两值不相等为true         不相等
      [ $num -ne $num2 ] && echo '不相等' || echo '相等'# 左边大于右边为true       num大于num2
      [ $num -gt $num2 ] && echo 'num大于num2' || echo 'num小于num2'# 左边小于右边为true       num大于num2
      [ $num -lt $num2 ] && echo 'num小于num2' || echo 'num大于num2'# 左边大于等于右边为true    num大于等于num2
      [ $num -ge $num2 ] && echo 'num大于等于num2' || echo 'num小于等于num2'# 左边小于等于右边为true    num大于等于num2
      [ $num -le $num2 ] && echo 'num小于等于num2' || echo 'num大于等于num2'
      

数组

数组元素的下标由0 开始编号,获取数组中的元素要利用下标

  • 定义数组

    • 方式一: array_name=(value1 value2 value3)

    • 方式二:

      ]# array_name[0]=value0
      ]# array_name[1]=value1
      ]# array_name[2]=value2
      
  • 读取数组

    符号作用
    ${value[index]}取出数组的索引值, 从0开始
    ${value[*]}获取数组中所有的值
    ${value[@]}获取数组中所有的值
    ${#value[@]}获取当前数组有多少个值
    ${#value[index]}获取单个字符串长度,不加index就是0
    ${value[*]::}${数组名[@或*]:开始下标:结束下标}
    ${value[*]//}数组值替换,不改变原字符串,${数组名[@或*]/模式/新值}
    • 示例

      ]# echo ${array_name[2]}    # 结果: value2
      ]# echo ${array_name[*]}    # 结果: value0 value1 value2
      ]# echo ${array_name[@]}    # 结果: value0 value1 value2]# echo ${#array_name[*]}   # 结果:3
      ]# echo ${#array_name[1]}   # 结果:6
      ]# echo ${#array_name}      # 结果: 6  默认下标为0]# echo ${array_name[@]:0:1}  # 结果: value0
      ]# echo ${array_name[@]:1}    # 结果: value1 value2]# echo ${array_name[@]/value1/test}   #  结果: value0 test value2# 数组遍历打印
      ]# for v in ${array_name[@]};do echo $v;done
      
      1. 如果该下标元素已经存在,会怎么样?

        答:会修改该下标的值为新的指定值。
        例如:array_name[2]=100,数组被修改为(value0 value1 100)
        
      2. 如果指定的下标已经超过当前数组的大小,如上述的arr_number的大小为2,指定下标为10或者11或者大于2的任意值会如何?

        答:新赋的值被追加到数组的尾部。
        例如:arr_number[13]=13,数组被修改为(value0 value1 value2 13)
        
      3. 判断字符是否存在列表中

        aaaa=[tcp,udp,TCP,UDP,Tcp,Udp]
        echo $aaa | grep -w "tcp1312323" >/dev/null && echo 111 || echo 222222
        
      4. 判断为数字

        [root@localhost opt]# grep '^[[:digit:]]*$' <<< "123123a" && echo "number" || echo "not"
        not
        
  • 删除操作

    • 清除某个元素: unset arr_number[1],这里清除下标为1的数组;
    • 清空整个数组: unset arr_number;
  • 追加元组元素

    因为shell 数组没有追加函数可以
    利用数组的长度来追加元素
    获取数组的长度:${#arr[*]}
     
    [root@localhost opt]# arr[0]=0
    [root@localhost opt]# arr[1]=1
    [root@localhost opt]# arr[2]=2
    [root@localhost opt]# echo ${#arr[*]}
    3
    
    [root@localhost opt]# for a in ${arr[@]};do echo ${a};done
    [root@localhost opt]# for ((i=0;i<${#arr[@]};i++));do echo ${arr[i]};done
    
  • 示例

    • 数组遍历

      IAM_SFVWLI=(x1 x2 x3)
      
      xx=`(grep "^IAM_SFVWLI"  x.conf | sed "s@^IAM_SFVWLI=((.*))@\1@")`
      # 获取数组元素字符串
      xx="${IAM_SFVWLI[*]}"
      
      # 使用空格作为分隔符拆分字符串为数组
      IFS=" " read -ra SERVERS <<< "$xx"
      
      # 遍历数组并处理元素
      for server in "${SERVERS[@]}"; do
          echo "$server"
      done
      
数组数据处理
  • 数组切片

    ${ARRAY[@]:offset:number}
        offset 要跳过的元素个数
        number  要取出的元素个数
        
    # 取偏移量之后的所有元素
    ${ARRAY[@]:offset}
    
    ~]# xiong=(a b c d e f g)
    ~]# echo ${xiong[@]:1:2}
        b c
    
    ~]# echo ${xiong[@]:1}
        b c d e f g
    
  • 向数组中追加元素

    ARRAY[${#ARRAY[*]}]=value
    
    ~]# xiong[${#xiong[1]}]=aaaa   # 在第一个索引位置添加
    ~]# echo ${xiong[@]}
    a aaaa c d e f g
    
  • 元组追加

    [root@localhost ~]# xx=()
    [root@localhost ~]# xx+=(11 22 33)
    [root@localhost ~]# echo ${xx[@]}
    11 22 33
    [root@localhost ~]# xx+=(44 55)
    [root@localhost ~]# echo ${xx[@]}
    11 22 33 44 55
    
字符串切片
  • 定义默认值

     ${var: -length}: # 注意:冒号后必须有一空白字符
    
    ~]# declare -i test  # 定义空数组
    ~]# echo ${test:-test}  # 当数组为空时会显示默认值
        test
    
  • 基于模式取子串

    ${var#*word}:  其中word可以是指定的任意字符
    功能:自左而右,查找var变量所存储的字符串中,第一次出现的word, 删除字符串开头至第一次出现word字符串(含)之间的所有字符 
    
    ${var##*word}:同上,贪婪模式,不同的是,删除的是字符串开头至最后一次由word指定的字符之间的所有内容
    
    示例
    ~]# file="/var/log/message"
    ~]# echo ${file#*/}   	# var/log/message    # 匹配第一个/
    ~]# echo ${file##*/}  	# message      	# 贪婪模式  匹配最后的一个/
    
    ${var%word*}:其中word可以是指定的任意字符
    功能:自右而左,查找var变量所存储的字符串中,第一次出现的word, 删除字符串最后一个字符向左至第一次出现word字符串(含)之间的所有字符
    
    ${var%%word*}:  同上,只不过删除字符串最右侧的字符向左至最后一次出现word字符之间的所有字符
    
    ~]# echo ${file%/*}  #   /var/log
    ~]# echo ${file%%/*}  #   空,  /右边无字符串
    

算术运算

  • 基础算术运算符

    用于整数的加、减、乘、除、取余等基本运算。

    运算符含义示例(使用 $((...)) 语法)
    +加法echo $((2 + 3)) 结果为5
    -减法echo $((10 - 4)) 结果为6
    *乘法(需转义或用 $((...))echo $((5 * 6)) 结果为30
    /除法(整数除法,向下取整)echo $((10 / 3)) 结果为3
    %取余(模运算)echo $((10 % 3)) 结果为1
  • 赋值运算符

    用于将运算结果赋值给变量,结合基础运算符使用。

    运算符含义示例说明
    =直接赋值a=5将 5 赋值给变量 a
    +=加法赋值a=5; a+=3(等价于 a=$((a+3))执行后 a 的值为 8
    -=减法赋值a=10; a-=4执行后 a 的值为 6
    *=乘法赋值a=2; a*=5执行后 a 的值为 10
    /=除法赋值a=10; a/=2执行后 a 的值为 5
  • 自增/自减运算符

    运算符含义示例(前置)示例(后置)说明
    ++自增 1a=3; echo $((++a)) → 4a=3; echo $((a++)) → 3前置:先自增再返回值;后置:先返回值再自增
    --自减 1a=3; echo $((--a)) → 2a=3; echo $((a--)) → 3前置:先自减再返回值;后置:先返回值再自减
  • 其它运算符

    运算符含义示例
    (())双小括号算术运算符,用于 替代expr 命令for((i=0;i<10;++i))或者((out=$a*$b))或者if(($a==$b));then ... fi,无需添加空格了,更加符合 C 的编程语法
    ,逗号运算符1、用在连接一连串的数学表达式中,这串数学表达式均被求值,但只有最后一个求值结果被返回。 2、用于参数替代中,表示首字母小写,如果是两个逗号,则表示全部小写,注意,这个特性在bash version 4的时候被添加的。
expr 算术运算
  • 参数

    运算类型运算符语法示例结果关键注意事项
    加法+expr 5 + 38运算符两侧必须有空格
    减法-expr 10 - 46同上
    乘法*expr 3 * 515乘号需用反斜杠``转义
    除法/expr 15 / 35仅支持整数除法,结果向下取整
    取余%expr 8 % 53返回除法后的余数
  • 示例

    ]# cat e1.sh 
    #!/bin/bash
    #
    num1=100
    num2=101
    x=`expr $num1 + $num2`       # 引用时注意左右空格          
    x1=`expr $num1 * $num2`     # 如果是 乘法 需要先转义一下
    x2=`expr $num1 - $num2`
    x3=`expr $num1 / $num2`
    x4=`expr $num1 % $num2`
    
    ]# expr 1 + 3   也需要注意左右空格, 计算时乘法需要先转义
    # expr 1+3 会直接打印 1+3
    
    # 判断是否为整形
    expr "$1" : '[0-9]*$' >/dev/null && echo "整数" || echo "非整数"
    
  • 判断是否数字

    expr $1 "+" 10 &>/dev/null
    [ $? -eq 0 ] && echo "数字" || echo "不是数字"
    
  • (())使用方法

    ]# cat e2.sh 
    #!/bin/bash
    #
    num1=100
    num2=191
    c1=$((num1+num2))
    c2=$((num1-num2))
    c3=$((num1*num2))
    c4=$((num1/num2))
    c5=$((num1%num2))
    
let 算术运算

let 命令取代并扩展了expr 命令的整数算术运, 如 num=100 num2=80

  • 参数

    运算符说明示例输出结果
    +加法let a=2+3; echo $a5
    -减法let b=10-4; echo $b6
    *乘法let c=3*5; echo $c15
    /除法(取整)let d=7/2; echo $d3
    %取余let e=10%3; echo $e1
    **幂运算let f=2**3; echo $f8
    ++后自增let x=5; let x++; echo $x6
    --后自减let y=5; let y--; echo $y4