• shell编程5-函数与正则表达式


    一.case语句

    case语句为多选择语句.可以用case语句匹配一个值与一个模式,如果匹配成功,执行相
    匹配的命令.

    case var in             定义变量;var代表是变量名
    pattern 1)              模式1;| 分割多个模式,相当于or
        command1            需要执行的语句
        ;;                  两个分号代表命令结束
    pattern 2)
        command2
        ;;
    pattern 3)
        command3
        ;;
    		  *)              default,不满足以上模式,默认执行*)下面的语句
        command4
        ;;
    esac							esac表示case语句结束
    

    案例1

    • 脚本提示让用户输入需要管理的服务名,然后提示用户需要对服务做什么操作,如启动,关闭,重启等
    [root@shell ~]# vim case.sh            
    #!/bin/bash
    read -p "请输入要操作的服务名称(nginx):" service
    read -p "请输入要对服务执行的操作:" action
    case $action in
            start|S)
            systemctl start $service
            echo "$service is running"
            ;;
            stop|T)
            systemctl stop $service
            echo "$service is stoped"
            ;;
            restart|R)
            systemctl restart $service
            echo "$service is restart"
            ;;
            *)
            echo "请输入正确的操作指令"
            ;;
    esac
    [root@shell ~]# source case.sh 
    请输入要操作的服务名称(nginx):vsftpd   
    请输入要对服务执行的操作:restart
    vsftpd is restart
    [root@shell ~]# source case.sh 
    请输入要操作的服务名称(nginx):nginx
    请输入要对服务执行的操作:status
    请输入正确的操作指令
    

    案例2

    • 模拟一个多任务维护界面.当执行程序时先显示总菜单,然后进行选择后做相应维护监控操作.
    [root@shell ~]# vim case2.sh 
    #!/bin/bash
    cat <<-EOF
            h       显示命令帮助
            f       显示磁盘分区
            d       显示磁盘挂载
            m       查看内存使用
            u       查看系统负载
            q       退出程序
    EOF
    # <<-EOF将忽略起止内容中前面的tab制表符,而<
    while true
    do
            read -p "请输入需要的选项:" var1
            case $var1 in
                    h)
                    cat <<-EOF
                            h       显示命令帮助
                            f       显示磁盘分区
                            d       显示磁盘挂载
                            m       查看内存使用
                            u       查看系统负载
                            q       退出程序
                    EOF
                    ;;
                    f)
                    fdisk -l
                    ;;
                    d)
                    df -h
                    ;;
                    m)
                    free -m
                    ;;
                    u)
                    untime
                    ;;
                    q)
                    echo "已退出程序"
                    break
                    ;;
    esac
    done
    [root@shell ~]# source case2.sh 
    h       显示命令帮助
    f       显示磁盘分区
    d       显示磁盘挂载
    m       查看内存使用
    u       查看系统负载
    q       退出程序
    请输入需要的选项:u
     16:33:40 up 2 days, 18 min,  1 user,  load average: 0.01, 0.02, 0.00
    请输入需要的选项:^C
    [root@shell ~]# source case2.sh 
    h       显示命令帮助
    f       显示磁盘分区
    d       显示磁盘挂载
    m       查看内存使用
    u       查看系统负载
    q       退出程序
    请输入需要的选项:u
     16:33:44 up 2 days, 18 min,  1 user,  load average: 0.01, 0.02, 0.00
    请输入需要的选项:q
    已退出程序
    

    二.函数

    shell中允许将一组命令集合或语句形成一段可用代码,这些代码块称为shell函数.给这段代码起个名字称为函数名,后续可以直接调用该段代码的功能.

    1.函数定义

    函数名()
    {
      函数体(一堆命令的集合,来实现某个功能)   
    }
    
    function 函数名()
    {
       函数体(一堆命令的集合,来实现某个功能)  
    }
    
    
    function_name() {
    		command
    		command
    }
    
    
    function function_name() {
    		command
    		command
    }
    
    
    函数中return说明:
    1.return可以结束一个函数,类似于前面讲的循环控制语句break(结束当前循环,执行循环体后面的代码)
    2.return默认返回函数中最后一个命令的退出状态,也可以给定参数值,该参数值的范围是0-256之间.
    3.如果没有return命令,函数将返回最后一个Shell的退出值.
    

    2.函数的调用

    • 当前命令行调用
    [root@shell-note ~]# vim fun1.sh
    hello(){
    #!/bin/bash
    hello(){
            echo "hello xiaomei $1"
    }       
    menu(){
            cat <<-EOF
            1. mysql
            2. web
            3. app
            4. exit
            EOF
    }
    [root@shell-note ~]# source fun1.sh 
    # 生效函数
    [root@shell-note ~]# hello 111
    # 调用函数
    hello xiaomei 111
    [root@shell-note ~]# menu 
    1. mysql
    2. web
    3. app
    4. exit
    
    • 定义到用户的环境变量中
    [root@shell-note ~]# vim .bashrc 
    # .bashrc
      
    # User specific aliases and functions
    
    alias rm='rm -i'
    alias cp='cp -i'
    alias mv='mv -i'
    
    # Source global definitions
    if [ -f /etc/bashrc ]; then
            . /etc/bashrc
    fi
    
    # 添加如下内容
    hello(){
            echo "hello xiaomei $1"
    }
    menu(){
            cat <<-EOF
            1. mysql
            2. web
            3. app
            4. exit
            EOF
    }
    [root@shell-note ~]# source .bashrc
    # 生效函数
    [root@shell-note ~]# hello 111
    hello xiaomei 111
    [root@shell-note ~]# menu 
    1. mysql
    2. web
    3. app
    4. exit
    
    • 脚本中调用
    [root@shell-note ~]# vim fun2.sh
    #!/bin/bash
    menu(){
            cat <<-EOF
            h       hello
            q       quit
            t       test
            EOF
    }       
    menu
    # 调用函数
    [root@shell-note ~]# source fun2.sh 
    h       hello
    q       quit
    t       test
    

    3.应用案例

    通过跳板机连接生产机器
    结合expect脚本

    [root@shell-note ~]# dnf -y install expect
    [root@shell-note ~]# vim jump-server.sh 
    #!/bin/bash
    while true
    do
    menu(){
            cat <<-EOF
            欢迎使用Jumper-server,请选择你要操作的主机:
            1. DB1-Master
            2. DB2-Slave
            3. Web1
            4. Web2
            h. help
            q. exit
            EOF
    }
    menu
    read -p "请选择你要操作的主机:" host
    case $host in
            1)
                    ./1.sh
                    ;;
            2)
                    ./2.sh
                    ;;
            3)
                    ./3.sh
                    ;;
            4)
                    ./4.sh
                    ;;
            h)
                    menu
                    ;;
            q)
                    break
                    ;;
    esac
    done
    [root@shell-note ~]# vim 1.sh 
    #!/usr/bin/expect
      
    spawn ssh root@172.20.251.124
    # 启动新的进程
    # 其余脚本皆为修改此处ip即可
    expect {
            "yes/no" { send "yes\r"; exp_continue }
            "password:" { send "000000\r" };
    }
    # expect用于从进程接受字符串
    # send用于向进程发送字符串
    interact
    # 允许用户交互
    
    [root@shell-note ~]# chmod +x {1..4}.sh
    # 增加expect脚本执行权限
    [root@shell-note ~]# sed -i "/expect {/i\set timeout 30" {1..4}.sh 
    # 添加超时处理
    [root@shell-note ~]# cat 1.sh 
    #!/usr/bin/expect
      
    spawn ssh root@172.20.251.124
    # 启动新的进程
    # 其余脚本皆为修改此处ip即可
    set timeout 30
    expect {
            "yes/no" { send "yes\r"; exp_continue }
            "password:" { send "000000\r" };
    }
    # expect用于从进程接受字符串
    # send用于向进程发送字符串
    interact
    # 允许用户交互
    [root@shell-note ~]# source jump-server.sh 
    # 测试脚本
    欢迎使用Jumper-server,请选择你要操作的主机:
    1. DB1-Master
    2. DB2-Slave
    3. Web1
    4. Web2
    h. help
    q. exit
    请选择你要操作的主机:4
    spawn ssh root@172.20.251.164
    root@172.20.251.164's password: 
    Activate the web console with: systemctl enable --now cockpit.socket
    
    Last login: Fri Sep 16 08:51:27 2022 from 172.20.251.192
    [root@shell ~]# 
    # 成功登录生产服务器
    

    实战不推荐,仅当练手项目,因为密码暴露在脚本中不是安全的

    四.正则表达式

    1.什么是正则表达式

    正则表达式(Regular Expression、regex或regexp,缩写为RE),也译为正规表示法、常规表示法,是一种字符模式,用于在查找过程中匹配指定的字符。

    许多程序设计语言都支持利用正则表达式进行字符串操作。例如,在Perl中就内建了一个功能强大的正则表达式引擎。

    正则表达式这个概念最初是由Unix中的工具软件(例如sed和grep)普及开的。

    支持正则表达式的程序如:locate |find| vim| grep| sed |awk

    2.第一类正则

    • 名词解释:

    元字符:指那些在正则表达式中具有特殊意义的专用字符,如:点(.) 星(*) 问号(?)等

    前导字符:即位于元字符前面的字符 abc* aooo.

    • 正则中常用的元字符
    (1).		任意单个字符,除了换行符 
    (2)*		前导字符出现0次或连续多次  ab*能匹配“a”,“ab”以及“abb”,但是不匹配“cb”
    (3).*		任意长度的字符  ab.*  ab123 abbb abab
    (4)^		行的开头
    (5)$		行的结尾
    (6)^$		空行
    
    
    (7)[]		匹配指定字符组内的任一单个字符   [abc]
    (8)[^]	匹配不在指定字符组内的任一字符 	[^abc]
    
    (9)^[]	匹配以指定字符组内的任一字符开头   ^[abc]
    (10)^[^]	匹配不以指定字符组内的任一字符开头  ^[^abc] 
    
    
    
    (11)\<		取单词的头
    (12)\>		取单词的尾
    (13)\<\>		精确匹配符号 	grep -w 'xxx'
    
    
    (14)\{n\}	匹配前导字符连续出现n次    go\{2\}   google  gooogle
    (15)\{n,\}	匹配前导字符至少出现n次 
    (16)\{n,m\}	匹配前导字符出现n次与m次之间 
    
    
     (17) \(strings\)	保存被匹配的字符
    
    [root@shell-note ~]# vim 1.txt
    172.20.251.1
    :%s/\(172.20.251\).1/\1.254/g  
    # 替换172.20.251.1为172.20.251.254
    172.20.251.254
    
    # sed -n 's#\(192\.168\)\.0\.254#\1\.1\.254#p'      
    找出含有192.168的行,同时保留192.168并标记为标签1,之后可以使用\1来引用它。最多可以定义9个标签,从左边开始编号,最左边的是第一个。
    \1 就代表被匹配到的第一个模式,sed 一共可以记录9个模式。
    [root@shell-note ~]# sed -n "s/\(192.168.10\).1/\1.254/p" 1.txt           
    192.168.10.254
    [root@shell-note ~]# sed -n "s/\(192.168\).10.1/\1.254/p" 1.txt 
    192.168.254
    
    将you are the hero 替换为we are family
    [root@shell-note ~]# vim 1.txt 
    10.1.1.254
    192.168.10.1
    you are hero
    :%s/you \(are\) the hero/we \1 \2family/p
    we are family
    # 同理,使用sed可替换为如下命令
    [root@shell-note ~]# sed -n "s/you \(are\) the hero/we \1 family/p" 1.txt    
    we are family
    [root@shell-note ~]# sed -n "s/you are the hero/we are family/p" 1.txt  
    we are family
    # -n为只显示匹配行,p将结果打印出来
    # 如果要改变文件内容使用i参数替换
    [root@shell-note ~]# sed -i "s/you \(are\) the hero/we \1 family/g" 1.txt  
    [root@shell-note ~]# cat 1.txt 
    10.1.1.254
    192.168.10.1
    we are family
    # 此处不使用-r参数,因为-r代表使用扩展正则表达式
    
    [0-9] [a-z] [A-Z] [a-zA-Z] [a-Z]
    =================================================================================
    Perl内置正则:
    \d      匹配数字  [0-9]
    \w      匹配字母数字下划线[a-zA-Z0-9_]
    \s      匹配空格、制表符、换页符[\t\r\n]
    
    #grep -P '\d' test.txt
    #grep -P '\w' test.txt
    #grep -P '\s' test.txt
    [root@shell-note ~]# grep -P "\d" 1.txt 
    10.1.1.254
    192.168.10.1
    [root@shell-note ~]# grep -P "\w" 1.txt  
    10.1.1.254
    192.168.10.1
    you are the hero
    [root@shell-note ~]# grep -P "\s" 1.txt  
    you are the hero
    
    • 扩展类的正则表达式 grep -E 或则 egrep
    扩展正则表达式元字符
    +			匹配一个或多个前导字符		bo+	boo bo
    ?			匹配零个或一个前导字符		bo?	b bo
    
    a|b		匹配a或b
    ()			组字符	   hello myself yourself     (my|your)self
    
    {n}		前导字符重复n次			\{n\}
    {n,}		前导字符重复至少n次	  \{n,\}
    {n,m}		前导字符重复n到m次		\{n,m\}
    
    [root@shell-note ~]# grep '[0-9]\{2\}\.[0-9]\{1\}\.[0-9]\{1\}\.[0-9]\{1\}' 1.txt 
    10.1.1.1
    [root@shell-note ~]# grep -E '[0-9]{2}\.[0-9]{1}\.[0-9]{1}\.[0-9]{1}' 1.txt
    10.1.1.1
    [root@shell-note ~]# grep -E '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' 1.txt 
    10.1.1.1
    192.168.10.1
    [root@shell-note ~]# grep -E '([0-9]{1,3}\.){3}[0-9]{1,3}' 1.txt 
    10.1.1.1
    192.168.10.1
    

    3.第二类正则

    表达式功能示例
    [:alnum:]字母与数字字符[[:alnum:]]+
    [:alpha:]字母字符(包括大小写字母)[[:alpha:]]{4}
    [:blank:]空格与制表符[[:blank:]]*
    [:digit:]数字[[:digit:]]?
    [:lower:]小写字母[[:lower:]]{4,}
    [:upper:]大写字母[[:upper:]]+
    [:punct:]标点符号[[:punct:]]
    [:space:]包括换行符,回车等在内的所有空白[[:space:]]+
    [root@shell-note ~]# grep -E '^[[:digit:]]+' 1.txt
    10.1.1.1
    192.168.10.1
    [root@shell-note ~]# grep -E '^[^[:digit:]]+' 1.txt
    you are the hero
    [root@shell-note ~]# grep -E '[[:lower:]]{4,}' 1.txt
    you are the hero
    

    4.实践操作

    [root@shell-note ~]# vim test.txt 
    1111111111
    2222222222
    
    
    2022-09-20
    helloworld
    welcome to test.
    
    192.168.10.254
    10.1.1.20
    AI2test
    S1mple
    tbase
    123@qq.com
    abc
    123456
    

    实现如下要求:

    1.查找不以大写字母开头的行
    2.查找有数字的行(两种写法)
    3.查找一个字母和一个数字连起来的
    4.查找不以t开头的行
    5.查找以数字开头的
    6.查找以大写字母开头的
    7.查找以小写字母开头的 
    8.查找以点结束的
    9.去掉空行
    10.去掉带空格的空行
    11.查找完全匹配123456的行
    12.查找ip并将最后的数字都替换为254
    13.查找hello开头的行并将之后内容替换为program
    14.统计/sbin/nologin在/etc/passwd里出现了几次
    15.找出全部是数字的行
    16.使用正则表达式给仅有数字行前加两0
    17.查找邮箱账号
    

    答案例(不止一种方法,结果正确即可)

    1.
    [root@shell-note ~]# grep "^[^A-Z]" test.txt 
    2.
    [root@shell-note ~]# grep "[0-9]" test.txt 
    [root@shell-note ~]# grep -E "[[:digit:]]+" test.txt
    3.
    [root@shell-note ~]# grep "[A-Za-z][0-9]" test.txt 
    4.
    [root@shell-note ~]# grep "^[^t]" test.txt
    5.
    [root@shell-note ~]# grep "^[^0-9]" test.txt
    6.
    [root@shell-note ~]# grep "^[A-Z]" test.txt 
    7.
    [root@shell-note ~]# grep "^[a-z]" test.txt 
    8.
    [root@shell-note ~]# grep "\.$" test.txt 
    # 记得给点加上转义字符
    9.
    [root@shell-note ~]# sed -i "/^$/d" test.txt 
    10.
    [root@shell-note ~]# sed -i "/^\s*$/d" test.txt
    11.
    [root@shell-note ~]# grep "123456" test.txt 
    123456
    12.
    [root@shell-note ~]# sed -n "s/\([0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\).[0-9]\{1,3\}/\1.254/p" test.txt 
    # 此处只为显示ip替换后的结果
    # 下面的命令才是替换文本中的ip内容
    [root@shell-note ~]# sed -i "s/\([0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\).[0-9]\{1,3\}/\1.254/g" test.txt
    13.
    [root@shell-note ~]# sed -n "s/\(^hello\).*/\1program/p" test.txt 
    [root@shell-note ~]# sed -i "s/\(^hello\).*/\1program/g" test.txt   
    14.
    [root@shell-note ~]# grep -c "/sbin/nologin" /etc/passwd
    15.
    [root@shell-note ~]# grep -x "[0-9]*" test.txt 
    # -x 参数会查找字符串 exact 的匹配,即全字匹配
    16.
    [root@shell-note ~]# sed -n "s/\(^[0-9][0-9]*[0-9]$\)/00\1/p" test.txt 
    001111111111
    002222222222
    00123456
    17.
    [root@shell-note ~]# grep "[0-9]*@[a-z0-9]*\.[a-z]*" test.txt 
    [root@shell-note ~]# grep -E '^[0-9]+@[a-z0-9]+\.[a-z]+$' test.txt
    
    # grep参数详解
    grep --help:
    匹配模式选择:
    Regexp selection and interpretation:
      -E, --extended-regexp     扩展正则
      -G, --basic-regexp        基本正则
      -P, --perl-regexp         调用perl的正则
      -e, --regexp=PATTERN      use PATTERN for matching
      -f, --file=FILE           obtain PATTERN from FILE
      -i, --ignore-case         忽略大小写
      -w, --word-regexp         匹配整个单词
    

    5.正则总结

    元字符:在正则中,具有特殊意义的专用字符 . *
    前导字符:元字符前面的字符叫前导字符

    元字符字符说明示例
    *前导字符出现0次或者连续多次ab* abbbb
    .除了换行符以外,任意单个字符ab. ab8 abu
    .*任意长度的字符ab.* adfdfdf
    []括号里的任意单个字符或一组单个字符[abc][0-9][a-z]
    [^]不匹配括号里的任意单个字符或一组单个字符[^abc]
    ^[]匹配以括号里的任意单个字符开头^[abc]
    ^[^]不匹配以括号里的任意单个字符开头^[^abc]
    ^行的开头^root
    $行的结尾bash$
    ^$空行
    \{n\}和{n}前导字符连续出现n次[0-9]\{3\}
    \{n,\}和{n,}前导字符至少出现n次[a-z]{4,}
    \{n,m\}和{n,m}前导字符连续出现n-m次go{2,4}
    \<\>精确匹配单词\
    " role="presentation">保留匹配到的字符hello" role="presentation">hello
    +前导字符出现1次或者多次[0-9]+
    ?前导字符出现0次或者1次go?
    |^root|^ftp
    ()组字符(hello|world)123
    \dperl内置正则grep -P \d+
    \w匹配字母数字下划线
  • 相关阅读:
    德国金融监管机构网站遭遇大规模DDoS攻击后“瘫痪”
    CG MAGIC分享3d Max中的Corona渲染器材质如何成转换VRay材质?
    在爬虫的时候发现request 中的from data 是一串数据格式
    CONTAINER: Few-Shot Named Entity Recognition via ContrastiveLearning
    ORACLE 实现字符串根据条件拼接
    Google Earth Engine —— 隐形错误get获取元素后结果无法筛选(字符串转数字函数)
    【数据结构(三)】单向环形链表和约瑟夫问题(3)
    面向对象编程(高级部分)——代码块
    Hadoop系列(一)Hadoop全分布式集群环境搭建
    pip出现的问题
  • 原文地址:https://blog.csdn.net/llllyh812/article/details/126837462