美文网首页
shell编程学习笔记(二)

shell编程学习笔记(二)

作者: dev_winner | 来源:发表于2020-10-25 16:07 被阅读0次
    • 内置命令:shift:使位置参数向左移动,默认移动1位,例如:shift 1
    #!/bin/env bash
    sum=0
    while [ $# -ne 0 ]
      do
        let sum=$sum+$1 # 始终加上第1个位置的参数
        shift(将位置参数向左移动1位)
      done
    echo sum=$sum
    ---------------------------------------------------------------------
    [root@localhost myshell]# vim shift.sh
    [root@localhost myshell]# chmod +x shift.sh 
    [root@localhost myshell]# ./shift.sh 
    sum=0
    [root@localhost myshell]# ./shift.sh 1 2 3
    sum=6
    [root@localhost myshell]# yum list | grep expect
    expect.x86_64                               5.45-14.el7_1              base     
    expect-devel.i686                           5.45-14.el7_1              base     
    expect-devel.x86_64                         5.45-14.el7_1              base     
    expectk.x86_64                              5.45-14.el7_1              base     
    pexpect.noarch                              2.3-11.el7                 base     
    [root@localhost myshell]# yum -y install expect(安装 expect 程序)
    [root@localhost myshell]# rpm -ql expect(列出expect安装包安装的文件)
    
    • expect是一门tcl(Tool Command Language)工具命令语言,其可以实现登录到远程服务器过程中自动应答。
    • exp_continue附加于某个expect判断项之后,可以使该项被匹配后,还能继续匹配该expect判断语句内的其他项。
    #!/usr/bin/env expect
    # 定义变量:set key value
    set ip 192.168.211.141
    set password 123456
    set timeout 5 # 定义超时时间
    # 开启一个程序
    spawn ssh root@$ip
    # 捕获相关内容
    expect {
      "(yes/no)?" { send "yes\r";exp_continue } # \r表示回车执行该命令
      "password:" { send "$password\r" }
    }
    # 让终端处于交互状态,即会保持在终端而不会退回到原终端
    interact
    ------------------------------------------------------------------------
    [root@localhost myshell]# vim expect1.sh 
    [root@localhost myshell]# ./expect1.sh 或者 expect -f expect1.sh 
    spawn ssh root@192.168.211.141
    root@192.168.211.141's password: 
    Last login: Sun Oct 25 15:56:57 2020 from 192.168.211.146
    [root@hadoop ~]# logout
    Connection to 192.168.211.141 closed.
    ------------------------------------------------------------------------
    #!/usr/bin/env expect
    # 定义变量,set key value
    set ip 192.168.211.141
    set password 123456
    set timeout 5
    # 开启一个程序
    spawn ssh root@$ip
    # 捕获相关内容
    expect {
      "(yes/no)?" { send "yes\r";exp_continue }
      "password:" { send "$password\r" }
    }
    expect "#"
    send "rm -rf /tmp/*\r"
    send "hostname\r"
    send "date +%F\r"
    send "touch /tmp/file{1..3}\r"
    expect eof # 表示交互结束,退回到原用户
    -------------------------------------------------------------------------
    [root@localhost myshell]# ./expect1.sh 
    spawn ssh root@192.168.211.141
    root@192.168.211.141's password: 
    Last login: Sun Oct 25 16:18:28 2020 from 192.168.211.146
    [root@hadoop ~]# rm -rf /tmp/*
    [root@hadoop ~]# hostname
    hadoop
    [root@hadoop ~]# date +%F
    2020-10-25
    [root@hadoop ~]# touch /tmp/file{1..3}
    [root@hadoop ~]# [root@localhost myshell]# (自动退出连接远程会话的终端程序)
    [root@hadoop tmp]# ll
    总用量 0
    -rw-r--r--. 1 root root 0 10月 25 16:18 file1
    -rw-r--r--. 1 root root 0 10月 25 16:18 file2
    -rw-r--r--. 1 root root 0 10月 25 16:18 file3
    ---------------------------------------------------------------------------
    #!/bin/env expect
    # 使用位置参数
    set ip [ lindex $argv 0 ]
    set password [ lindex $argv 1 ]
    set timeout 5
    # 开启一个程序
    spawn ssh root@$ip
    # 捕获相关内容
    expect {
      "(yes/no)?" { send "yes\r";exp_continue }
      "password:" { send "$password\r" }
    }
    interact
    ---------------------------------------------------------------------------
    [root@localhost myshell]# ./expect2.sh 192.168.211.141 123456
    spawn ssh root@192.168.211.141
    root@192.168.211.141's password: 
    Last login: Sun Oct 25 16:19:53 2020 from 192.168.211.146
    [root@hadoop ~]# logout
    Connection to 192.168.211.141 closed.
    [root@localhost myshell]# cat 1.txt 
    10.1.1.1 stu1
    [root@localhost myshell]# read ip password < 1.txt (将文件1.txt中的第一行内容按格式赋值给变量ip和password)
    [root@localhost myshell]# echo $ip $password 
    10.1.1.1 stu1
    [root@localhost myshell]# cat 1.txt 
    10.1.1.1 stu1
    10.1.1.2 stu2
    [root@localhost myshell]# while read ip password;do echo $ip $password;done < 1.txt 
    10.1.1.1 stu1
    10.1.1.2 stu2
    
    • 在多台服务器上各创建1个用户(shell和expect结合使用):
    #!/bin/env bash
    # 循环在指定服务器上创建用户和删除/tmp目录下的所有文件
    while read ip password
      do
        /usr/bin/expect <<END &>/dev/null
        spawn ssh root@$ip
        expect {
              "(yes/no)?" { send "yes\r";exp_continue }
              "password:" { send "$password\r" }
        }
        expect "#" { send "useradd yy1;rm -rf /tmp/*;logout\r" }
        expect eof
    END # 注意 END 需要顶格写
        echo "服务器:$ip 已完成创建用户!"       
      done < ip.txt
    ------------------------------------------------------------------------------
    [root@localhost myshell]# vim expect3.sh
    [root@localhost myshell]# chmod +x expect3.sh 
    [root@localhost myshell]# cat ip.txt 
    192.168.211.141 123456
    192.168.211.156 123456
    [root@localhost myshell]# ./expect3.sh 
    服务器:192.168.211.141 已完成创建用户yy1!
    服务器:192.168.211.156 已完成创建用户yy1!
    [root@hadoop tmp]# id yy1(第一个服务器)
    uid=1002(yy1) gid=1002(yy1) 组=1002(yy1)
    [root@openstack ~]# id yy1(第二个服务器)
    uid=1871(yy1) gid=1871(yy1) groups=1871(yy1)
    
    • 运维人员实现批量推送公钥到内网主机:①管理员root创建yunwei用户和安装expect软件包,编写脚本文件(normal.sh)
    #!/bin/env bash
    # 判断 jumper 上的 yunwei 账号是否存在
    {
      id yunwei
      [ $? -ne 0 ] && useradd yunwei && echo 123 | passwd --stdin yunwei
    } &>/dev/null
    # 判断 expect 程序是否安装
    rpm -q expect
    [ $? -ne 0 ] && yum -y install expect && echo "expect软件包已安装成功!"
    --------------------------------------------------------------------------------
    [root@localhost myshell]# vim normal.sh
    [root@localhost myshell]# chmod +x normal.sh 
    [root@localhost myshell]# userdel -r yunwei
    [root@localhost myshell]# rpm -e expect(卸载 expect 软件包)
    [root@localhost myshell]# ./normal.sh 
    未安装软件包 expect 
    已加载插件:fastestmirror, langpacks
    ......
    完毕!
    expect软件已成功安装!
    [root@localhost myshell]# rpm -q expect
    expect-5.45-14.el7_1.x86_64
    
    • ②运维人员检查公钥是否存在,哪些内网主机可以ping通,并将公钥推送到能ping通的内网主机,编写脚本文件:(push_public_key.sh)
    #!/bin/env bash
    home_dir=/home/yunwei
    # 判断 yunwei 用户的密钥对是否存在
    [ ! -f $home_dir/.ssh/id_rsa.pub ] && ssh-keygen -P '' -f $home_dir/.ssh/id_rsa &>/dev/null
    # 循环检查局域网内主机的网络并对连通的主机进行公钥推送
    ip_txt=$home_dir/ip.txt
    for i in `cat $ip_txt` # 读取文件中每一行内容
      do
        ip=`echo $i | cut -d : -f1`
        password=`echo $i | cut -d: -f2`
        ping -c1 $ip &>/dev/null
        if [ $? -eq 0 ];then
            echo $ip >> ~/ip_up.txt
            /usr/bin/expect <<-END
            set timeout 10
            spawn ssh-copy-id root@$ip # 开启一个程序,将公钥推送到指定内网主机
            expect "(yes/no)?" { send "yes\r";exp_continue }
            expect "password:" { send "$password\r" }
            expect eof
            END # 注意 END 左边为一个 tab 制表符:\t
        else
            echo $ip >> $home_dir/ip_down.txt
        fi
      done
    -----------------------------------------------------------------
    [yunwei@localhost ~]$ cat ip.txt 
    192.168.211.156:123456
    192.168.211.141:123456
    [yunwei@localhost ~]$ chmod +x push_public_key.sh
    [yunwei@localhost ~]$ bash -x push_public_key.sh
    [yunwei@localhost ~]$ bash -x push_public_key.sh 
    + home_dir=/home/yunwei
    + '[' '!' -f /home/yunwei/.ssh/id_rsa.pub ']'
    + ssh-keygen -P '' -f /home/yunwei/.ssh/id_rsa
    ......
    Now try logging into the machine, with:   "ssh 'root@192.168.211.141'"
    and check to make sure that only the key(s) you wanted were added.
    
    [yunwei@localhost ~]$ ll
    总用量 12
    -rw-rw-r--. 1 yunwei yunwei  46 10月 25 21:17 ip.txt
    -rw-rw-r--. 1 yunwei yunwei  64 10月 25 22:05 ip_up.txt
    -rwxrwxr-x. 1 yunwei yunwei 723 10月 25 22:05 push_public_key.sh
    [yunwei@localhost ~]$ cat ip_up.txt 
    192.168.211.156
    192.168.211.141
    [yunwei@localhost ~]$ ll .ssh/
    总用量 12
    -rw-------. 1 yunwei yunwei 1675 10月 25 22:05 id_rsa
    -rw-r--r--. 1 yunwei yunwei  410 10月 25 22:05 id_rsa.pub
    -rw-r--r--. 1 yunwei yunwei  354 10月 25 22:05 known_hosts
    [root@hadoop .ssh]# ll(服务器ip为:192.168.211.141)
    总用量 4
    -rw-------. 1 root root 410 10月 25 22:05 authorized_keys
    [root@openstack .ssh]# ll(服务器ip为:192.168.211.156)
    total 4
    -rw------- 1 root root 410 Oct 25 22:05 authorized_keys
    --------------------------------------------------------------------
    #!/bin/env bash
    # 测试验证公钥是否推送成功
    remote_ip=`tail -1 ~/ip_up.txt`
    ssh root@$remote_ip hostname &>/dev/null
    test $? -eq 0 && echo "成功推送公钥完毕!"
    --------------------------------------------------------------------
    [yunwei@localhost ~]$ vim test_ssh.sh
    [yunwei@localhost ~]$ chmod +x test_ssh.sh 
    [yunwei@localhost ~]$ ./test_ssh.sh 
    成功推送公钥完毕!
    
    • 使用visudo打开sudo配置文件来管理用户的sudo权限:
    ## Allow root to run any commands anywhere
    root    ALL=(ALL)       ALL
    yunwei  ALL=(root)      NOPASSWD:ALL,!/sbin/shutdown,!/sbin/init,!/bin/rm -rf /
    --------------------------------------------------------------------------------------------
    1、第一个字段 yunwei 指定的是用户:用户名或别名。每个用户设置一行,多个用户设置多行,也可将多个用户设置成一个别名后再进行设置。
    2、第二个字段 ALL 指定用户所在的主机:ip或主机名,表示该sudo设置只在该主机上生效,默认限制的是本机,ALL表示在所有主机上都生效!
    3、第三个字段 (root) 括号里指定以什么用户身份使用 sudo,即使用sudo后可以享有root账号下所有权限。若要排除个别用户,则可在括号内设置,如 ALL=(ALL,!oracle,!pos)。
    4、第四个字段 ALL 指定的是执行的命令:即使用 sudo 后可以执行所有命令。除了设置不能关机和删除根内容以外,也可设置别名。NOPASSWD:ALL 表示使用sudo后不需要输入密码。
    5、也可以授权给一个用户组:%admin ALL=(ALL) ALL  表示 admin 组里的所有成员都可以在任何主机上以任何用户身份执行任何命令。
    
    • 普通数组:只能使用整数作为数组索引。如:array[0]=v1
    • 关联数组:可以使用字符串作为数组索引。
    • 普通数组的定义,一次赋予多个值:数组名=(值1 值2 值3 ...),如:将文件中按空格隔开的字符串内容赋值给array1数组:array1=($(cat /etc/passwd))array4=(1 2 3 4 "hello world" [10]=linux)array3=(harry amy jack "Miss Hou")
    • 获取某个下标的数组元素:${数组名[元素下标]}
    • 查看所有普通数组:declare -a
    • 查看所有关联数组:declare -A
    • 获取数组中所有元素:echo ${array[*]}echo ${array[@]}
    • 获取数组中元素总个数:echo ${#array[*]}
    • 获取数组中元素的索引下标:echo ${!array[@]}
    • 访问指定的子数组:echo ${array[@]:1:2}(1代表从下标为1的元素开始获取;2代表获取后面几个元素)。
    [root@localhost myshell]# names=(harry jack tom sarsh)
    [root@localhost myshell]# echo ${names[*]}
    harry jack tom sarsh
    [root@localhost myshell]# echo ${names[@]}
    harry jack tom sarsh
    [root@localhost myshell]# echo ${names[@]:1:2}(获取子数组所有元素)
    jack tom
    [root@localhost myshell]# echo ${#names[*]}(获取数组中元素总个数)
    4
    [root@localhost myshell]# echo ${!names[*]}(获取每个元素对应的数组索引)
    0 1 2 3
    
    • 关联数组的定义:①首先要声明关联数组:declare -A asso_array1;②数组元素赋值:一次赋一个值:数组名[索引or下标]=变量值,如:asso_array1[linux]=one;一次赋多个值:asso_array2=([name1]=harry [name2]=jack [name3]=amy [name4]="Miss Hou")。注意:关联数组下标是无序的。建议数组下标不要带空格!
    [root@localhost myshell]# declare -A asso_array1
    [root@localhost myshell]# asso_array1[linux]=one
    [root@localhost myshell]# asso_array1[java]=two
    [root@localhost myshell]# declare -A
    declare -A BASH_ALIASES='()'
    declare -A BASH_CMDS='()'
    declare -A asso_array1='([java]="two" [linux]="one" )'
    [root@localhost myshell]# echo ${asso_array1[*]}
    two one
    [root@localhost myshell]# echo ${!asso_array1[*]}
    java linux # 从这里可以看出初始化顺序不一定是打印顺序,即元素下标存储是无序的
    [root@localhost myshell]# echo ${#asso_array1[*]}
    2
    [root@localhost myshell]# declare -A asso_array2
    [root@localhost myshell]# asso_array2=([name1]=harry [name2]=jack [name3]=amy [name4]="Miss Hou")
    [root@localhost myshell]# declare -A
    declare -A BASH_ALIASES='()'
    declare -A BASH_CMDS='()'
    declare -A asso_array1='([java]="two" [linux]="one" )'
    declare -A asso_array2='([name3]="amy" [name2]="jack" [name1]="harry" [name4]="Miss Hou" )'
    [root@localhost myshell]# echo ${asso_array2[*]}
    amy jack harry Miss Hou
    [root@localhost myshell]# echo ${!asso_array2[*]}
    name3 name2 name1 name4
    [root@localhost myshell]# echo ${#asso_array2[*]}
    4
    
    • 获取某个文件名:basename 文件的绝对路径名
    • 获取某个文件所在的目录名:dirname 文件的绝对路径名
    • 变量值的删除匹配模式:
    1个"%"代表从右往左去掉一个/key/
    2个"%"代表从右往左最大去掉/key/
    1个"#"代表从左往右去掉一个/key/
    2个"#"代表从左往右最大去掉/key/
    -----------------------------------------------
    [root@localhost myshell]# url=www.taobao.com
    [root@localhost myshell]# echo ${#url} (获取变量的长度)
    14
    [root@localhost myshell]# echo ${url#*.}(从左往右去掉一个/*./)
    taobao.com
    [root@localhost myshell]# echo ${url##*.}(从左往右最大去掉/*./)
    com
    [root@localhost myshell]# echo ${url%.*}(从右往左去掉一个/.*/)
    www.taobao
    [root@localhost myshell]# echo ${url%%.*}(从右往左最大去掉/.*/)
    www
    [root@localhost myshell]# A=/root/myshell/web_server_check.sh 
    [root@localhost myshell]# echo $A
    /root/myshell/web_server_check.sh
    [root@localhost myshell]# dirname $A(获取文件名)
    /root/myshell
    [root@localhost myshell]# basename $A(获取文件所在的目录名)
    web_server_check.sh
    
    • 统计web服务的不同连接状态个数,编写脚本(count_web_state.sh):
    #!/bin/env bash
    # 统计每个连接状态的个数
    declare -A array1
    states=`ss -ant | grep 80 | cut -d' ' -f1`
    for i in $states
      do
            let array1[$i]++
      done
    # 通过遍历数组中的所有索引,将索引和对应的值打印出来
    for j in ${!array1[@]}
      do
            echo $j:${array1[$j]}
      done
    -----------------------------------------------------------------------
    [root@localhost myshell]# vim count_web_state.sh
    [root@localhost myshell]# chmod +x count_web_state.sh 
    [root@localhost myshell]# bash -x count_web_state.sh 
    + declare -A array1
    ++ ss -ant
    ++ grep 80
    ++ cut '-d ' -f1
    + states=LISTEN
    + for i in '$states'
    + let 'array1[LISTEN]++'
    + for j in '${!array1[@]}'
    + echo LISTEN:1
    LISTEN:1
    [root@localhost myshell]# netstat -ntp
    Active Internet connections (w/o servers)
    Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name    
    tcp        0      0 192.168.211.146:22      192.168.211.1:14311     ESTABLISHED 3509/sshd: root@pts 
    [root@localhost myshell]# netstat -nltp | grep 80
    tcp6       0      0 :::80                   :::*                    LISTEN      6845/httpd          
    [root@localhost myshell]# netstat -natp | grep 80
    tcp6       0      0 :::80                   :::*                    LISTEN      6845/httpd          
    [root@localhost myshell]# ss -natp | grep :80
    LISTEN     0      128       [::]:80                    [::]:*                   users:(("httpd",pid=6850,fd=4),("httpd",pid=6849,fd=4),("httpd",pid=6848,fd=4),("httpd",pid=6847,fd=4),("httpd",pid=6846,fd=4),("httpd",pid=6845,fd=4))
    
    • netstat用于显示网络连接,路由表,接口状态,伪装连接,网络链路信息和组播成员组:netstat [选项] | grep 端口号
    • 常用选项:
    -a(--all):显示所有正在或不在侦听的套接字。加上 --interfaces 选项将显示没有标记的接口。
    -t(--tcp):显示tcp相关选项。
    -u(--udp):显示udp相关选项。
    -n(--numeric):显示数字形式地址而不是去解析主机、端口或用户名。
    -l( --listening):只显示正在侦听的套接字(这是默认的选项)。
    -p(--program):显示套接字所属进程的PID和名称。
    
    • case语句为多重匹配语句,pattern表示需要匹配的模式。
    case var in # 定义变量,var 代表是变量名
    pattern 1)   # 模式1:用 | 或 or 分割多个候选值
        command1          
        ;; # 两个分号代表该命令执行完后就结束
    pattern 2)
        command2
        ;;
    pattern 3)
        command3
        ;;
            *)  # default:不满足以上模式,默认执行 *) 下面的命令
        command4
        ;;
    esac # esac表示 case 语句结束
    
    • 实战:执行脚本命令时传不同值完成不同的事情。
    #!/bin/env bash
    case $1 in
            start|S)
              echo "service is running..."
              ;;
            stop|T)
              echo "service is stopped..."
              ;;
            reload|R)
              echo "service is restart..."
              ;;
            *)
              echo "请输入你要执行的动作!"
              ;;
    esac
    ---------------------------------------------------------------------
    [root@localhost myshell]# vim case1.sh
    [root@localhost myshell]# chmod +x case1.sh 
    [root@localhost myshell]# ./case1.sh S
    service is running...
    [root@localhost myshell]# ./case1.sh T
    service is stopped...
    [root@localhost myshell]# ./case1.sh 
    请输入你要执行的动作!
    [root@localhost myshell]# ./case1.sh R
    service is restart...
    [root@localhost myshell]# cat 
    hello
    hello
    world 
    world(ctrl+d 结束输入)
    [root@localhost myshell]# cat <<END
    > hello
    > world
    > END(输入END结束输入)
    hello
    world
    [root@localhost myshell]# su - yunwei <<END
    > echo hello
    > END
    上一次登录:一 10月 26 16:01:32 CST 2020
    hello
    [root@localhost myshell]# (仍留在当前用户)
    
    • 实战:模拟一个多任务维护界面,当执行程序时先显示总菜单,然后进行选择后做出相应的维护操作。
    #!/bin/env bash
    menu(){
    cat <<-EOF
            h 显示命令帮助
            f 显示磁盘分区
            d 显示磁盘挂载
            m 查看内存使用
            u 查看系统负载
            q 退出程序
            EOF
    }
    # 函数调用
    menu
    while true
    do
    # 用户选择需要操作的内容
    read -p "请选择需要执行的动作(help h):" action
    clear
    # 函数调用
    menu
    case $action in
            h|help)
            menu
            ;;
            f)
            # fdisk -l
            lsblk
            ;;
            d)
            df -h
            ;;
            m)
            free -m
            ;;
            u)
            uptime
            ;;
            q)
            exit
            ;;
    esac
    done
    
    • 函数的定义:
    函数名() {
      函数体(一堆命令的集合)
    }
    -----------------------------
    function 函数名() {
      函数体(一堆命令的集合)
    }
    -----------------------------------------------------------
    [root@localhost myshell]# source fun1.sh (source 读取一下文件,函数的作用域只对当前终端有效)
    [root@localhost myshell]# cat fun1.sh 
    #!/bin/env bash
    hello() {
        echo hello world
    }
    function work() {
        rm -rf /tmp/*
        touch /tmp/file{1..3}
    }
    [root@localhost myshell]# hello
    hello world
    [root@localhost myshell]# work
    [root@localhost myshell]# ll /tmp/
    总用量 0
    -rw-r--r--. 1 root root 0 10月 26 16:42 file1
    -rw-r--r--. 1 root root 0 10月 26 16:42 file2
    -rw-r--r--. 1 root root 0 10月 26 16:42 file3
    
    • 函数中return的作用:①return可以结束一个函数;②return默认返回函数中最后一个命令的状态值,也可以给定参数值,范围是0-256之间;③若没有return命令,函数则默认返回最后一个指令的退出状态值。
    [root@localhost myshell]# vim fun1.sh 
    [root@localhost myshell]# source fun1.sh 
    [root@localhost myshell]# cat fun1.sh 
    #!/bin/env bash
    hello() {
        echo hello world
    }
    function work() {
        rm -rf /tmp/*
        touch /tmp/file{1..3}
        return 10  # 自定义返回最后一个命令执行后的状态值
    }
    [root@localhost myshell]# work 
    [root@localhost myshell]# echo $?
    10
    [root@localhost myshell]# vim fun2.sh 
    [root@localhost myshell]# source fun2.sh 
    [root@localhost myshell]# cat fun2.sh 
    #!/bin/env bash
    # 菜单打印
    menu(){
        cat <<-EOF
            h 显示命令帮助
            f 显示磁盘分区
            d 显示磁盘挂载
            m 查看内存使用
            u 查看系统负载
            q 退出程序
        EOF
    
    }
    hha(){
        echo $1
    }
    [root@localhost myshell]# hha 5555 444(执行某个函数时顺便传递位置参数)
    5555
    [root@localhost myshell]# su - stu1
    [stu1@localhost ~]$ vim ~/.bashrc # 将函数定义到用户的环境变量中
    [stu1@localhost ~]$ ls -a
    .  ..  .bash_logout  .bash_profile  .bashrc  .cache  .config  .mozilla  .viminfo
    [stu1@localhost ~]$ source ./.bashrc # 函数定义只在当前用户全局内有效:~/.bashrc,在任何用户全局内有效:/etc/bashrc)
    [stu1@localhost ~]$ menu
            h 显示命令帮助
            f 显示磁盘分区
            d 显示磁盘挂载
            m 查看内存使用
            u 查看系统负载
            q 退出程序
    
    • 实战:①只允许yunwei用户通过跳板机远程连接后台的应用服务器做一些维护操作;②公司运维人员远程通过yunwei用户连接跳板机时,跳出以下菜单供选择;③当用户选择相应主机后,直接免密码登录成功;④若用户不输入则一直提示用户输入,直到用户选择退出为止。编写脚本文件(jumper-server.sh):
    #!/bin/env bash
    menu()
    {
    cat <<-EOF
    欢迎使用Jumper-server,请选择你要操作的主机:
    1. DB1-Master
    2. DB2-Slave
    3. Web1
    4. Web2
    h. help
    q. exit
            EOF
    }
    # 屏蔽以下信号,比如键入 ctrl+z 后不作反应
    trap "" 1 2 3 19 20
    # 调用函数来打印菜单
    menu
    #循环等待用户选择
    while true
    do
    # 菜单选择,case...esac语句
    read -p "请选择你要访问的主机:" host
    case $host in
            1)
            ssh root@192.168.211.141
            ;;
            2)
            ssh root@192.168.211.156
            ;;
            h)
            clear;menu
            ;;
            q)
            exit
            ;;
    esac
    ---------------------------------------------------------------------------
    [yunwei@localhost ~]$ chmod +x jumper-server.sh
    [yunwei@localhost ~]$ vim .bashrc
    [root@localhost yunwei]# cat /home/yunwei/.bashrc 
    # .bashrc
    # Source global definitions
    if [ -f /etc/bashrc ]; then
        . /etc/bashrc
    fi
    # Uncomment the following line if you don't like systemctl's auto-paging feature:
    # export SYSTEMD_PAGER=
    # User specific aliases and functions
    ~/jumper-server.sh # 当登录到 yunwei 账号时直接进入自定义的菜单选择模式
    exit
    [root@localhost myshell]# su - yunwei(切换到 yunwei 账号时自动执行自定义的脚本)
    上一次登录:二 10月 27 14:58:10 CST 2020pts/1 上
    欢迎使用Jumper-server,请选择你要操作的主机:
    1. DB1-Master
    2. DB2-Slave
    3. Web1
    4. Web2
    h. help
    q. exit
    请选择你要访问的主机:1
    Last login: Tue Oct 27 15:18:27 2020 from 192.168.211.146
    [root@hadoop ~]# pwd
    /root
    [root@hadoop ~]# logout
    Connection to 192.168.211.141 closed.
    请选择你要访问的主机:^Z^Z^Z^Z^Z^C^C(快捷键信号被屏蔽,键入后不作反应)
    请选择你要访问的主机:q
    [root@localhost myshell]# 
    
    • 运行shell脚本时,若键入快捷键Ctrl+cCtrl+x(x为其他字符),程序就会终止运行,但我们并不希望shell脚本在运行时被信号中断,此时就可以使用屏蔽信号手段,让程序忽略用户输入的信号指令,从而继续运行shell脚本程序:trap "commands" signal-list(当脚本收到signal-list清单内列出的信号时,trap命令执行双引号中的命令)
    • trap "" signal-list:指定一个空命令串,表示直接忽视给定的信号。
    • 使用kill -l查看信号列表:
    1、SIGHUP:重新加载配置    
    2、SIGINT:键盘中断^C
    3、SIGQUIT:键盘退出
    9、SIGKILL:强制终止
    15、SIGTERM:终止(正常结束),缺省信号
    18、SIGCONT:继续
    19、SIGSTOP:停止
    20、SIGTSTP:暂停^Z
    
    • 正则表达式:①元字符:具有特殊意义的专用字符,如:点(.) 星(*) 问号(?)等。②前导字符:位于元字符前面的字符:abc*,aooo.
    • 正则表达式中常用的元字符:
    元字符 功能 备注
    . 匹配除换行符以外的任意单个字符
    * 前导字符出现0次或连续多次
    .* 任意长度的字符 ab.*
    ^ 行首(以......开头) ^root
    $ 行尾(以......结尾) bash$
    ^$ 空行
    [] 匹配括号里任意单个字符或一组单个字符 [abc]
    [^] 匹配不包含括号里任一单个字符或一组单个字符 [^abc]
    ^[] 匹配以括号里任意单个字符或一组单个字符开头 ^[abc]
    ^[^] 匹配不以括号里任意单个字符或一组单个字符开头 ^[^abc]
    \< 取单词的头 \<hel
    \> 取单词的尾 rld\>
    \<\> 精确匹配 \<world\>
    \{n\} 匹配前导字符连续出现n次 go\{2\}
    \{n,\} 匹配前导字符至少出现n次 go\{2,\}
    \{n,m\} 匹配前导字符出现n次与m次之间 go\{2,4\}
    \(\) 保存被匹配的字符 一般用于搜索替换
    \d 匹配数字(grep -P [0-9]
    \w 匹配字母数字下划线(grep -P [a-zA-Z0-9_]
    \s 匹配空格、制表符、换页符(grep -P [\t\r\n]
    vim替换ip地址字符串
    • 扩展类正则表达式中常用的元字符:必须使用特定的命指令:grep -Eegrepsed -r
    扩展元字符 功能 备注
    + 匹配1个或多个前导字符 bo+匹配boobo
    ? 匹配0个或1个前导字符 bo?匹配bbo
    | 匹配a或b
    () 组字符(看成整体) (my|your)self:表示匹配myselfyourself
    {n} 前导字符重复n次 go{2}
    {n,} 前导字符重复至少n次 go{2,}
    {n,m} 前导字符重复n到m次 go{2,4}
    [:lower:] 小写字母 [[:lower:]]{4,}
    [:upper:] 大写字母 [[:upper:]]+
    [:digit:] 数字 [[:digit:]]?
    [:alnum:] 字母和数字字符 [[:alnum:]]+
    [:alpha:] 所有字母(包括大小写字母) [[:alpha:]]{4}
    [:blank:] 空格与制表符 [[:blank:]]*
    [:punct:] 标点符号 [[:punct:]]
    [:space:] 包括换行符,回车等在内的所有空白 [[:space:]]+
    1、查找不以大写字母开头的行:
    grep '^[^A-Z]' re.sh
    grep -v '^[A-Z]' re.sh
    grep '^[^[:upper:]]' re.sh
    2、查找有数字的行:
    grep '[0-9]' re.sh
    grep -P '\d' re.sh
    3、查找数字相邻为字母的行:
    grep -E '[0-9][a-zA-Z]|[a-zA-Z][0-9]' re.sh
    4、查找不以字符r开头的行:
    grep -v '^r' re.sh
    grep '^[^r]' re.sh
    5、查找以数字开头的行:
    grep '^[0-9]' re.sh
    6、查找以大写字母开头的行:
    grep '^[A-Z]' re.sh
    7、查找以小写字母开头的行:
    grep '^[a-z]' re.sh
    8、查找以点结束的行:
    grep '\.$' re.sh
    9、查找不包行空行的所有行:
    grep -v '^$' re.sh
    10、查找完全匹配abc的行:
    grep '\<abc\>' re.sh 
    grep -w 'abc' re.sh
    11、查找A后有三个数字的行:
    grep -E 'A[0-9]{3}' re.sh
    grep 'A[0-9]\{3\}' re.sh
    12、统计root在/etc/passwd里出现了几次:
    grep -o 'root' /etc/passwd | wc -l
    13、找出主机网卡 ens32 的IP地址、广播地址、子网掩码:
    ifconfig ens32 | grep -o -E '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'
    ifconfig ens32 | grep -o -E '([0-9]{1,3}\.){3}[0-9]{1,3}'
    ifconfig ens32 | grep -o -P '(\d{1,3}\.){3}\d{1,3}'
    14、找出一行中全部是数字的所有行:
    grep -E '^[0-9]+$' re.sh
    15、找出邮箱地址的行:
    grep -E '^[0-9a-zA-Z]+@[a-z0-9]+\.[a-z]+$' re.sh
    

    相关文章

      网友评论

          本文标题:shell编程学习笔记(二)

          本文链接:https://www.haomeiwen.com/subject/dkggmktx.html