zoukankan      html  css  js  c++  java
  • 7.shell脚本编程

    1.shell 脚本语言的基本用法

    1.1shell 脚本创建

    1.格式要求:首行shebang机制

    #!/bin/bash
    #!/usr/bin/python
    #!/usr/bin/perl 
    

    2.添加执行权限,在命令行上指定脚本的绝对或者相对路径,也可以运行脚本解释器直接运行脚本

    1.2脚本的注释规范

    1、第一行一般为调用使用的语言

    2、程序名,避免更改文件名为无法找到正确的文件

    3、版本号

    4、更改后的时间

    5、作者相关信息

    6、该程序的作用,及注意事项

    7、最后是各版本的更新简要说明

    1.3脚本的执行方式

    [root@centos8 data]# cat shell.sh 
    #!/bin/bash
    ls
    [root@centos8 data]# chmod +x shell.sh 
    #相对路径执行
    [root@centos8 data]# ./shell.sh 
    a.txt  b.txt  linxu.txt  message  passwd  shell.sh  systeminfo.sh  test  vimrc
    #bash执行
    [root@centos8 data]# bash shell.sh 
    a.txt  b.txt  linxu.txt  message  passwd  shell.sh  systeminfo.sh  test  vimrc
    #执行远程主机的shell脚本
    [root@centos8 data]# #curl -s http://10.0.0.8/hello.sh|bash
    [root@centos8 ~]#wget -qO - http://www.wangxiaochun.com/testdir/hello.sh |bash
    

    1.4shell脚本调试

    脚本常见的3种错误:

    • 语法错误,后续的命令不继续执行
    • 命令错误,默认后续的命令还会继续执行,用bash -n 无法检查出来 ,可以使用 bash -x 进行观察
    • 逻辑错误,只能使用 bash -x 进行观察

    bash命令

    bash -n:只检测脚本中的语法错误,但无法检查出命令错误,但不真正执行脚本

    bash -x:调试并执行脚本

    1.5变量

    1.5.1 shell变量类型

    内置变量,如:PS1,PATH,UID,HOSTNAME,$$,BASHPID,PPID,$?,HISTSIZE

    用户自定义变量

    1.5.2 变量的定义和引用
    • 普通变量:生效范围为当前shell进程;对当前shell之外的其它shell进程,包括当前shell的子shell 进程均无效
    • 环境变量:生效范围为当前shell进程及其子进程
    • 本地变量:生效范围为当前shell进程中某代码片断,通常指函数

    变量赋值

    #变量的赋值可以是以下多种形式
    直接字串:name='root'
    变量引用:name="$USER"
    命令引用:name=`COMMAND` 或者 name=$(COMMAND)
    

    变量引用

    $name
    ${name}
    

    范例

    [root@centos8 data]# name='xiaoming'
    [root@centos8 data]# echo $name
    xiaoming
    
    [root@centos8 data]# user='yonghu'
    [root@centos8 data]# name="$user"
    [root@centos8 data]# echo $name
    yonghu
    
    [root@centos8 data]# name=`ls -l /data/`
    [root@centos8 data]# echo $name
    total 8 -rw-r--r-- 1 root root 403 Jan 2 17:39 systeminfo.sh -rw-r--r-- 1 root root 110 Jan 3 10:17 test
    [root@centos8 data]# echo "$name"
    total 8
    -rw-r--r-- 1 root root 403 Jan  2 17:39 systeminfo.sh
    -rw-r--r-- 1 root root 110 Jan  3 10:17 test
    

    范例:变量追加值

    [root@centos8 data]# echo $user
    yonghu
    [root@centos8 data]# user+=:xiaoming
    [root@centos8 data]# echo $user
    yonghu:xiaoming
    

    显示和删除变量

    #set查看所有变量
    #unset <name>删除指定变量
    #使用上述方式设置变量是临时保存于内存当中,当退出终端就会销毁
    
    1.5.3 环境变量

    说明

    • 子进程(包括孙子进程)可以继承父进程的变量,但是无法让父进程使用子进程的变量
    • 一旦子进程修改从父进程继承的变量,将会新的值传递给孙子进程
    • 一般只在系统配置文件中使用,在脚本中较少使用

    环境变量的声明和赋值

    #声明并赋值
    export name=VALUE
    declare -x name=VALUE
    #或者分两步实现
    name=VALUE
    export name
    

    变量引用:

    $name
    ${name}
    

    显示所有环境变量:

    env
    printenv
    export
    declare -x	
    

    删除变量

    unset				
    

    bash的内建环境变量

    PATH
    SHELL
    USER
    UID
    HOME
    PWD
    SHLVL #shell的嵌套层数,即深度
    LANG
    MAIL
    HOSTNAME
    HISTSIZE
    _   #下划线 表示前一命令的最后一个参数
    
    1.5.4 只读变量

    只能声明定义,但后续不能修改和删除,即常量;退出当前终端可以销毁声明的只读变量

    声明只读变量:

    readonly name
    declare -r name
    

    查看只读变量

    readonly [-p]
    declare -r
    
    1.5.5 变量位置

    在bash shell中内置的变量, 在脚本代码中调用通过命令行传递给脚本的参数

    $1, $2, ... 对应第1个、第2个等参数,shift [n]换位置
    $0 命令本身,包括路径
    $* 传递给脚本的所有参数,全部参数合为一个字符串
    $@ 传递给脚本的所有参数,每个参数为独立字符串
    $# 传递给脚本的参数的个数
    #清空所有位置变量
    set --
    

    范例

    [root@centos8 data]# cat arg.sh 
    #!/bin/bash
    echo "arg1 is $1"
    echo "arg1 is $2"
    echo "arg1 is $3"
    echo "the number of arg is $#"
    echo "all args is $*"
    echo "all args is $@"
    echo "the script name is `basename $0`"
    
    [root@centos8 data]# bash arg.sh {1..5}
    arg1 is 1
    arg1 is 2
    arg1 is 3
    the number of arg is 5
    all args is 1 2 3 4 5
    all args is 1 2 3 4 5
    the script name is arg.sh
    

    rm命令修改

    #用户使用rm命令时不删除文件而是移动到/tmp文件夹
    [root@centos8 data]# cat rm.sh 
    COLOR="echo -e \E[1;31m"
    END="\E[0m"
    DIR=/tmp/`date +%F_%H-%M-%S`
    mkdir $DIR
    mv $* $DIR
    $COLOR move $* to $DIR $END
    
    1.5.6 退出状态码

    $?

    $?的值为0,代表成功

    $?的值是1到255 #代表失败

    注意:

    • 如果未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码
    1.5.7 脚本安全和 set

    $- 变量

    [root@centos8 data]# echo $-
    himBHs
    

    h:hashall,打开选项后,Shell 会将命令所在的路径hash下来,避免每次都要查询。通过set +h将h选 项关闭

    i:interactive-comments,包含这个选项说明当前的 shell 是一个交互式的 shell。所谓的交互式shell, 在脚本中,i选项是关闭的 m:monitor,打开监控模式,就可以通过Job control来控制进程的停止、继续,后台或者前台执行等

    B:braceexpand,大括号扩展

    H:history,H选项打开,可以展开历史列表中的命令,可以通过!感叹号来完成,例如“!!”返回上最近的 一个历史命令,“!n”返回第 n 个历史命令

    set 命令

    -u 在扩展一个没有设置的变量时,显示错误信息, 等同set -o nounset

    -e 如果一个命令返回一个非0退出状态值(失败)就退出, 等同set -o errexit

    -o option 显示,打开或者关闭选项

    ​ 显示选项:set -o

    ​ 打开选项:set -o 选项

    ​ 关闭选项:set +o 选项

    1.6 printf

    printf:格式化要输出的数据

    printf FORMAT [ARGUMENT]...
    

    常用格式替换符

    • %s 字符串

    • %f 浮点格式

    • %b 相对应的参数中包含转义字符时,可以使用此替换符进行替换,对应的转义字符会被转 义

    • %c ASCII字符,即显示对应参数的第一个字符

    • %d,%i 十进制整数

    • %o 八进制值

    • %u 不带正负号的十进制值

    • %x 十六进制值(a-f)

    • %X 十六进制值(A-F)

    • %% 表示%本身

    • %s 中的数字代表此替换符中的输出字符宽度,不足补空格,默认是右对齐,%-10s表示10个字 符宽,- 表示左对齐

    常用转义字符

    • \a 警告字符,通常为ASCII的BEL字符
    • \n 换行
    • \r 回车
    • \t 水平制表符
    • \v 垂直制表符

    范例

    [root@centos8 data]# printf "%d\n" 123 321 123
    123
    321
    123
    
    [root@centos8 data]# printf "(%s) " 1 2 3
    (1) (2) (3) [root@centos8 data]# 
    [root@centos8 data]# printf "(%s) " 1 2 3;echo 
    (1) (2) (3) 
    [root@centos8 data]# printf "(%s) " 1 2 3;echo ""
    (1) (2) (3) 
    
    
    [root@centos8 data]# printf "%10s" name;echo
          name
    

    1.7算术运算

    shell 支持算术运算,但只支持整数,不支持小数

    shell运算符

    +
    -
    *
    /
    % 取模,即取余数,示例:9%4=1,5%3=2
    ** 乘方
    

    实现算数运算的方式

    1.	let var=算数表达式
    2.	((var=算数表达式))
    3.	var=$[算数表达式]
    4.	var=$((算数表达式))
    5.	delare -i var = 数值
    6. echo '算术表达式' | bc 
    7.	var=$(expr arg1 arg2 arg3 ...)
    

    范例

    #生成 1 - 50 之间随机数
    [root@centos8 data]# echo $[RANDOM%50+1]
    12
    

    增强型赋值:

    += i+=10 相当于 i=i+10
    -= i-=j   相当于 i=i-j
    *=
    /=
    %=
    ++ i++,++i   相当于 i=i+1
    -- i--,--i   相当于 i=i-1
    

    范例

    #i++和++i
    [root@centos8 ~]#unset i j ; i=1; let j=i++; echo "i=$i,j=$j"
    i=2,j=1
    [root@centos8 ~]#unset i j ; i=1; let j=++i; echo "i=$i,j=$j"
    i=2,j=2
    
    #使用计算器实现浮点运算
    [root@centos8 data]# echo "scale=3;20/3" | bc
    6.666
    

    1.8逻辑运算

    与:&:和0相与,结果为0,和1相与,结果保留原值

    1 与 1 = 1 
    1 与 0 = 0 
    0 与 1 = 0 
    0 与 0 = 0
    

    或:|:和1相或结果为1,和0相或,结果保留原值

     1 或 1 = 1
     1 或 0 = 1
     0 或 1 = 1
     0 或 0 = 0 
    

    非:!

     ! 1 = 0 ! true
     ! 0 = 1 ! false
    

    异或:^

    相同为假,不同为真。

    1 ^ 1 = 0
    1 ^ 0 = 1
    0 ^ 1 = 1
    0 ^ 0 = 0 
    

    范例

    #注意区分逻辑运算的二进制值与$?的返回值含义刚好相反
    #利用异或可以实现两个变量的数值交换
    [root@centos8 data]# x=30;y=20;x=$[x^y];y=$[x^y];x=$[y^x];echo "x=$x y=$y"
    x=20 y=30
    

    短路运算

    • 短路与

      CMD1 短路与 CMD2

      第一个CMD1结果为假 (0),总的结果必定为0,因此不需要执行CMD2

    • 短路或

      CMD1 短路或 CMD2

      第一个CMD1结果为真 (1),总的结果必定为1,因此不需要执行CMD2

    1.9条件测试命令

    若真,则状态码变量 $? 返回0

    若假,则状态码变量 $? 返回1

    条件测试命令:

    • test EXPRESSION
    • [ EXPRESSION ] #和test 等价,建议使用 [ ]
    • [[ EXPRESSION ]]

    查看帮助

    [root@centos8 data]# help [
    [root@centos8 data]# help test
    选项
    		-a FILE        True if file exists.
          -b FILE        True if file is block special.
          -c FILE        True if file is character special.
          -d FILE        True if file is a directory.
          -e FILE        True if file exists.
          -f FILE        True if file exists and is a regular file.
          -g FILE        True if file is set-group-id.
          -h FILE        True if file is a symbolic link.
          -L FILE        True if file is a symbolic link.
          -k FILE        True if file has its `sticky' bit set.
          -p FILE        True if file is a named pipe.
          -r FILE        True if file is readable by you.
          -s FILE        True if file exists and is not empty.
          -S FILE        True if file is a socket.
          -t FD          True if FD is opened on a terminal.
          -u FILE        True if the file is set-user-id.
          -w FILE        True if the file is writable by you.
          -x FILE        True if the file is executable by you.
          -O FILE        True if the file is effectively owned by you.
          -G FILE        True if the file is effectively owned by your group.
          -N FILE        True if the file has been modified since it was last read.
        
        String operators:
        
          -z STRING      True if string is empty.
        
          -n STRING
             STRING      True if string is not empty.
        
          STRING1 = STRING2
                         True if the strings are equal.
          STRING1 != STRING2
                         True if the strings are not equal.
          STRING1 < STRING2
                         True if STRING1 sorts before STRING2 lexicographically.
          STRING1 > STRING2
                         True if STRING1 sorts after STRING2 lexicographically.
       
        Other operators:
    #变量测试    
          -o OPTION      True if the shell option OPTION is enabled.
          -v VAR         True if the shell variable VAR is set.
          -R VAR         True if the shell variable VAR is set and is a name
                         reference.
          ! EXPR         True if expr is false.
          EXPR1 -a EXPR2 True if both expr1 AND expr2 are true.
          EXPR1 -o EXPR2 True if either expr1 OR expr2 is true.
    #数值测试    
          arg1 OP arg2   Arithmetic tests.  OP is one of -eq, -ne,
                         -lt, -le, -gt, or -ge.
        
        Arithmetic binary operators return true if ARG1 is equal, not-equal,
        less-than, less-than-or-equal, greater-than, or greater-than-or-equal
        than ARG2.
    
    
    1.9.1变量测试

    范例

    #判断 NAME 变量是否定义
    [ -v NAME ] 
    #判断 NAME 变量是否定义并且是否引用
    [ -R NAME ]
    
    [root@centos8 data]# echo $name
    
    [root@centos8 data]# [ -v name ]
    [root@centos8 data]# echo $?
    1
    [root@centos8 data]# name="xiaoming"
    [root@centos8 data]# [ -v name ]
    [root@centos8 data]# echo $?
    0
    
    [root@centos8 data]# [ -R name ]
    [root@centos8 data]# echo $?
    1
    [root@centos8 data]# [ -v name ]
    [root@centos8 data]# echo $?
    0
    
    1.9.1数值测试

    范例

    [root@centos8 data]# i=10;j=20;
    [root@centos8 data]# [ i -eq j ]
    -bash: [: i: integer expression expected
    [root@centos8 data]# [ $i -eq $j ]
    [root@centos8 data]# echo $?
    1
    
    1.9.2字符串测试

    范例

    [root@centos8 data]# echo $name
    
    [root@centos8 data]# [ -z $name ]
    [root@centos8 data]# echo $?
    0
    [root@centos8 data]# unset name
    [root@centos8 data]# [ -z $name ]
    [root@centos8 data]# echo $?
    0
    [root@centos8 data]# name=xiaoming
    [root@centos8 data]# [ -z "$name" ]
    [root@centos8 data]# echo $?
    1
    

    [ ]用法

    [[]] 用法,建议,当使用正则表达式或通配符使用,一般情况使用 [ ]
    
    == 左侧字符串是否和右侧的PATTERN相同
     注意:此表达式用于[[ ]]中,PATTERN为通配符
    
    =~ 左侧字符串是否能够被右侧的正则表达式的PATTERN所匹配
     注意: 此表达式用于[[ ]]中;扩展的正则表达式
    

    范例

    [root@centos8 data]# NAME=linux
    [root@centos8 data]# [[ "$NAME" == linu\* ]]
    [root@centos8 data]# echo $?
    1
    [root@centos8 data]# [[ "$NAME" == linu* ]]
    [root@centos8 data]# echo $?
    0
    #结论:[[ == ]] == 右侧的 * 做为通配符,不要加“”,只想做为*, 需要加“” 或转义
    
    1.9.3 文件测试

    范例

    [root@centos8 data]# [ -a /data/test ]
    [root@centos8 data]# echo $?
    0
    [root@centos8 data]# ![ -a /data/test ]
    [ -a /data/test ] -a /data/test ]
    -bash: [: too many arguments
    [root@centos8 data]# ! [ -a /data/test ]
    [root@centos8 data]# echo $?
    1
    [root@centos8 data]# [ -w /etc/shadow ]
    [root@centos8 data]# echo $?
    0
    

    1.10 () 和 {}

    (CMD1;CMD2;...)和 { CMD1;CMD2;...; } 都可以将多个命令组合在一起,批量执行。使用man bash可以查看帮助

    区别

    ( list ) 会开启子shell,并且list中变量赋值及内部命令执行后,将不再影响后续的环境

    { list; } 不会启子shell, 在当前shell中运行,会影响当前shell环境

    范例

    [root@centos8 data]# echo $BASHPID
    164220
    [root@centos8 data]# ( echo $BASHPID;sleep 100)
    164450
    sshd(976)───sshd(164206)───sshd(164219)───bash(164220)─┬─bash(164449)───sleep(164450)
    [root@centos8 data]# echo $BASHPID
    164220
    [root@centos8 data]# { echo $BASHPID;}
    16422
    

    1.11 组合测试条件

    1.11.1 第一种方式

    ​ [ EXPRESSION1 -a EXPRESSION2 ] 并且,EXPRESSION1和EXPRESSION2都是真,结果才为真

    ​ [ EXPRESSION1 -o EXPRESSION2 ] 或者,EXPRESSION1和EXPRESSION2只要有一个真,结果就为 真

    ​ [ ! EXPRESSION ] 取反

    范例

    [root@centos8 data]# FILE=/data/test
    [root@centos8 data]# [ -f $FILE -a -x $FILE ]
    [root@centos8 data]# echo $?
    1
    [root@centos8 data]# chmod +x /data/test
    [root@centos8 data]# ll
    total 20
    -rw-r--r-- 1 root root   0 Jan  9 22:28 a.log
    -rw-r--r-- 1 root root 181 Jan  4 18:29 arg.sh
    -rw-r--r-- 1 root root   1 Jan  6 19:17 lx.txt
    -rw-r--r-- 1 root root 116 Jan  5 15:50 rm.sh
    -rw-r--r-- 1 root root 403 Jan  2 17:39 systeminfo.sh
    -rwxr-xr-x 1 root root 110 Jan  3 10:17 test
    [root@centos8 data]# [ -f $FILE -a -x $FILE ]
    [root@centos8 data]# echo $?
    0
    
    1.11.2 第二种方式

    COMMAND1 && COMMAND2 并且,短路与,代表条件性的AND THEN 如果COMMAND1 成功,将执行COMMAND2,否则,将不执行COMMAND2

    COMMAND1 || COMMAND2 或者,短路或,代表条件性的OR ELSE 如果COMMAND1 成功,将不执行COMMAND2,否则,将执行COMMAND2

    ! COMMAND #非,取反

    范例

    [root@centos8 data]# test "A" = "B" && echo "string are equal"
    [root@centos8 data]# test "A" = "A" && echo "string are equal"
    string are equal
    
    [root@centos8 data]# ll
    -rw-r--r-- 1 root root 110 Jan  3 10:17 test.sh
    [root@centos8 data]# [ -f $FILE ] && [[ $FILE =~ \.sh$ ]] && chmod +x $FILE
    
    [root@centos8 data]# ll
    -rwxr-xr-x 1 root root 110 Jan  3 10:17 test.sh
    
    

    &&与||组合使用

    [root@centos8 data]# NAME=user;id $NAME && echo "$NAME is exist" || echo "$NAME is not exist"
    uid=1001(user) gid=1001(user) groups=1001(user)
    user is exist
    #注意:如果&& 和 || 混合使用,&& 要在前,|| 放在后。否则会出现逻辑错误
    

    1.12 read命令

    read:使用read来把输入值分配给一个或多个shell变量,read从标准输入中读取值,给每个单词分配一个变 量,所有剩余单词都被分配给最后一个变量,如果变量名没有指定,默认标准输入的值赋值给系统内置 变量REPLY

    read [options] [name ...]
    

    常用选项

    • -p 指定要显示的提示
    • -s 静默输入,一般用于密码
    • -n N 指定输入的字符长度N
    • -d '字符' 输入结束符
    • -t N TIMEOUT为N秒

    范例

    [root@centos8 data]# read
    username
    [root@centos8 data]# echo $REPLY
    username
    [root@centos8 data]# read NAME SEX
    xiaoming boy
    [root@centos8 data]# echo $NAME $SEX
    xiaoming boy
    [root@centos8 data]# read -p "please input your name:" NAME
    please input your name:daxiong 
    [root@centos8 data]# echo $NAME
    daxiong
    
    面试题 read和输入重定向
    [root@centos8 data]# echo 1 2 | read x y;echo $x $y
    
    [root@centos8 data]# echo 1 2 | (read x y;echo $x $y)
    1 2
    #Each command in a pipeline is executed as a separate process (i.e., in  a subshell)
    #管道符把每个命令执行都在独立的子进程中,所以第一种情况父进程无法读取子进程的变量
    

    2 bash的配置文件

    2.1 配置文件分类

    全局生效配置文件

    /etc/profile
    /etc/profile.d/*.sh
    /etc/bashrc
    

    个人用户生效配置文件

    ~/.bash_profile
    ~/.bashrc
    

    2.2 配置文件加载顺序

    2.2.1 交互式登录顺序
    • 直接通过终端输入账号密码登录
    • 使用 su - UserName 切换的用户

    配置文件加载顺序

    /etc/profile.d/*.sh
    /etc/bashrc
    /etc/profile
    /etc/bashrc    #此文件执行两次
    .bashrc
    .bash_profile
    
    2.2.2 非交互式顺序
    • su UserName
    • 图形界面下打开的终端
    • 执行脚本
    • 任何其他的bash实例

    配置文件加载顺序

    /etc/profile.d/*.sh
    /etc/bashrc
    .bashrc
    

    2.3 配置文件分类

    2.3.1 Profile类

    为交互式登录的shell提供配置

    全局:/etc/profile, /etc/profile.d/*.sh
    个人:~/.bash_profile
    

    用途:

    1)用于定义环境变量

    2)运行脚本或命令

    2.3.2 Bashrc类

    为非交互式和交互式登录的shell提供配置

    全局:/etc/bashrc
    个人:~/.bashrc
    

    用途:

    1)定义命令别名和函数

    2)定义本地变量

    2.3.3 配置文件生效方式
    1. 重新启动shell进程
    2. source|. 配置文件

    3.流程控制

    3.1 条件判断if

    格式

    if: if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else COMMANDS; ] fi
    

    范例

    #BMI指数
    [root@centos8 script]# cat bmi.sh 
    #!/bin/bash
    #
    #********************************************************************
    #Author:		yuanyuan
    #QQ: 			1136132605
    #Date: 			2021-01-11
    #FileName:		bmi.sh
    #URL: 			http://www.bestvae.cn
    #Description:		The test script
    #Copyright (C): 	2021 All rights reserved
    #********************************************************************
    read -p "请输入身高(单位m)" HIGH
    if [[ ! "$HIGH" =~ ^[0-2]\.?[0-9]{,2}$ ]];then
            echo "输入身高错误"
            exit 1
    fi
    read -p "请输入体重(单位kg)" WEIGHT
    if [[ ! "$WEIGHT" =~ ^[0-9]{1,3}$ ]];then
            echo "输入体重错误"
            exit 1
    fi
    BMI=`echo $WEIGHT/$HIGH^2|bc`
    if [ "$BMI" -le 18 ];then
            echo "你太瘦了多吃点"
    elif [ "$BMI" -lt 24 ];then
            echo "你的身材很棒"
    else
            echo "你太胖了,加强运动"
    fi
    

    3.2 条件判断 case 语句

    格式

    case: case WORD in [PATTERN [| PATTERN]...) COMMANDS ;;]... esac
        Execute commands based on pattern matching.	
        
    #case支持全局通配符
    *: 任意长度任意字符
    ?: 任意单个字符
    []:指定范围内的任意单个字符
    |:   或,如 a或b
    

    范例

    #运维表
    #!/bin/bash
    echo -en "\E[$[RANDOM%7+31];1m"
    cat <<EOF
    1)备份数据库
    2)清理日志
    3)软件升级
    4)软件回退
    5)删库跑路
    EOF
    echo -en '\E[0m'
    read -p "请输入上面的数字:" MENU
    if [[ ! "$MENU" =~ ^[1-5]$ ]];then
            echo "你的输入有误"
            exit 1
    fi
    case $MENU in
    1)
        echo "备份成功";;
    2)
        echo "清理日志";;                  
    3)
        echo "清理日志";;        
    4)
        echo "清理日志";;        
    5)          
        echo "清理日志";;                  
    esac
    

    练习

    1、编写脚本 createuser.sh,实现如下功能:使用一个用户名做为参数,如果指定参数的用户存在,就 显示其存在,否则添加之。并设置初始密码为123456,显示添加的用户的id号等信息,在此新用户第一 次登录时,会提示用户立即改密码,如果没有参数,就提示:请输入用户名

    [root@centos8 script]# cat creatuser.sh 
    #!/bin/bash
    
    if [ -z "$1" ];then
        echo "请输入用户名字格式为$0 username"
        exit 1
    else
        getent passwd $1 > /dev/null
        if [ "$?" -eq 0 ];then
            echo "$1 is exist" 
            echo `getent passwd $1`
            exit 1
        else
            useradd $1
            echo '123456' | passwd --stdin $1 > /dev/null
            passwd -e $1 > /dev/null
            echo -e "$1用户创建成功初始密码为123456\n`getent passwd $1`"                                                                           
       fi
    fi
    

    2、编写生成脚本基本格式的脚本,包括作者,联系方式,版本,时间,描述等

    [root@centos8 script]# cat /root/.vimrc 
    set ts=4
    set expandtab
    set ignorecase
    set cursorline
    set autoindent
    set showcmd
    autocmd BufNewFile *.sh exec ":call SetTitle()"
    func SetTitle()
    	if expand("%:e") == 'sh'
    	call setline(1,"#!/bin/bash") 
    	call setline(2,"#") 
    	call setline(3,"#********************************************************************") 
    	call setline(4,"#Author:	    yuanyuan") 
    	call setline(5,"#QQ: 			1136132605") 
    	call setline(6,"#Date: 			".strftime("%Y-%m-%d"))
    	call setline(7,"#FileName:		".expand("%"))
    	call setline(8,"#URL: 			http://www.bestvae.cn")
    	call setline(9,"#Description:		The test script") 
    	call setline(10,"#Copyright (C): 	".strftime("%Y")." All rights reserved")
    	call setline(11,"#********************************************************************") 
    	call setline(12,"") 
    	endif
    endfunc
    autocmd BufNewFile * normal G
    

    3.3 循环

    3.3.2 for循环

    格式:

    #格式1
    for NAME [in WORDS ... ] ; do COMMANDS; done
    #格式2
    for (( 控制变量初始化;条件判断表达式;控制变量的修正表达式 )); do COMMANDS; done
    

    循环列表生成方式:

    • 直接给出列表

    • 整数列表

      {start..end}

      $(seq [start [step]] end)

    • 返回列表的命令

      $(COMMAND)

    • 使用*.sh

    • 变量引用,如:$@,$*

    范例:

    #使用两种方式计算1+...+100的结果
    [root@centos8 ~]$sum=0;seq -s+ 100 | bc
    5050
    [root@centos8 ~]$sum=0;for i in {1..100};do sum=$[i+sum];done;echo $sum
    5050
    
    #九九乘法表
    [root@centos8 script]$vim for_9.sh
    for i in {1..9};do
        for j in `seq $i`;do                               
            echo -e "${j}x${i}=$[i*j]\t\c"
        done
        echo 
    done
    
    #使用另一种方式输出等腰三角形
    [root@centos8 script]$vim for_trian.sh
    read -p "输入等腰三角形的行数:" line
    for ((i=1;i<=line;i++));do
        for ((k=0;k<=line-i;k++));do                                                                                               
            echo -e ' \c'
        done
        for ((j=1;j<=2*i-1;j++));do
            echo -e '*\c'
        done
        echo
    done
    
    3.3.2 while循环

    格式:

    while COMMANDS; do COMMANDS; done
    
    while CONDITION; do
     	循环体
    done
    #进入条件:CONDITION为true
    #当循环次数不确定时建议使用while
    
    3.3.3 until 循环

    格式:

    until COMMANDS; do COMMANDS; done
    until CONDITION; do
     		循环体
    done
    
    #进入条件: CONDITION 为false
    #退出条件: CONDITION 为true
    

    3.3.4 循环控制语句 continue

    continue [N]:提前结束第N层的本轮循环,而直接进入下一轮判断;最内层为第1层

    范例 :

    [root@centos8 script]$vim continue.sh
    for ((i=0;i<=10;i++));do
        for ((j=0;j<10;j++));do
            [ $j -eq 5 ] && continue 2            
            echo -e $j'\c'
        done
        echo ----------------------
    done
    [root@centos8 script]$bash continue.sh 
    0123401234012340123401234012340123401234012340123401234 
    
    3.3.5 循环控制语句 break

    break [N]:提前结束第N层整个循环,最内层为第1层

    范例:

    [root@centos8 script]$vim continue.sh
    for ((i=0;i<=10;i++));do
        for ((j=0;j<10;j++));do
            [ $j -eq 6 ] && break 2                       
            echo -e $j'\c'
        done
        echo ----------------------
    done
    [root@centos8 script]$bash continue.sh 
    012345
    
    3.3.6 循环控制 shift 命令

    shift [n] 用于将参量列表 list 左移指定次数,缺省为左移一次。

    范例:

    [root@centos8 script]$cat doit.sh 
    #!/bin/bash
    until [ -z "$1" ]
    do 
        echo "$1"
        shift
    done
    echo
    [root@centos8 script]$bash doit.sh 1 2 3 4 5
    1
    2
    3
    4
    5
    

    3.3.7 select 循环与菜单

    格式:

    select NAME [in WORDS ... ;] do COMMANDS; done
    
    select variable in list ;do
     			循环体命令
    done
    
    • select 循环主要用于创建菜单,按数字顺序排列的菜单项显示在标准错误上,并显示 PS3 提示符,等待用户输入
    • 用户输入被保存在内置变量 REPLY 中
    • select 是个无限循环,因此要用 break 命令退出循环,或用 exit 命令终止脚本。也可以按 ctrl+c 退出循环

    范例:

    [root@centos8 script]$cat select.sh
    #!/bin/bash
    sum=0
    PS3="请输入1-6"
    select menu in 北京烤鸭 佛跳墙 小龙虾 羊蝎子 火锅 点菜结束 ;do
        case $REPLY in 
        1)
            echo $menu 价格是100
            let sum+=100
            ;;
        2)
            echo $menu 价格是88
            let sum+=88
            ;;
        3)
            echo $menu 价格是10
            let sum+=10
            ;;
        4)
            echo $menu 价格是190
            let sum+=190
            ;;
        5)
            echo $menu 价格是17
            let sum+=17
            ;;
        6)
            break;;
            esac
       done
       echo "$sum"
    
    

    4 函数

    Shell程序在子Shell中运行,而Shell函数在当前Shell中运行。因此在当前Shell中,函数可对shell中变量 进行修改

    4.1 函数管理

    4.1.1 定义函数
    #语法一:
    function name { 
    	COMMANDS ; 
    } 
    #语法二:
    function name () { 
    	COMMANDS ; 
    }
    #语法三:
    func_name (){
     	COMMANDS ;
    }
    
    4.1.2 查看函数
    #显示已定义函数名称及定义
    delcare -f
    #显示已定义函数名称
    delcare -F
    

    4.1.3 删除函数

    unset func_name
    

    4.2 函数调用

    4.2.1 交互式环境函数调用

    范例:

    [root@centos8 script]$dir() { ls -l;}
    [root@centos8 script]$dir
    total 44
    -rw-r--r-- 1 root root 482 Mar  4 11:39 continue.sh
    
    [root@centos8 script]$declare -f dir
    dir () 
    { 
        ls --color=auto -l
    }
    
    4.2.2 脚本中定义及使用

    注意:函数使用前需先定义

    [root@centos8 script]$bash function.sh 
    hello
    [root@centos8 script]$cat function.sh 
    #!/bin/bash
    function hello(){
        echo "hello"
    }
    hello
    
    4.2.3 调用函数文件

    格式:

    . filename	或 source filename	
    

    范例:

    [root@centos8 script]$. function
    [root@centos8 script]$hello
    hello
    [root@centos8 script]$cat function 
    #!/bin/bash
    function hello(){
        echo "hello"
    }
    

    4.3 环境函数

    定义:

    export -f function_name
    declare -xf function_name
    

    查看:

    export -f
    declare -xf
    

    4.4 函数参数

    • 传递参数给函数:在函数名后面以空白分隔给定参数列表即可
    • 在函数体中当中,可使用$1, $2, ...调用这些参数;还可以使用$@, $*, $#等特殊变量,函数名称存储于$FUNCNAME变量中

    4.5 函数中变量有效范围

    • 普通变量:只在当前shell进程有效,为执行脚本会启动专用子shell进程;因此,本地变量的作用 范围是当前shell脚本程序文件,包括脚本中的函数
    • 环境变量:当前shell和子shell有效
    • 本地变量:函数的生命周期;函数结束时变量被自动销毁

    由于普通变量和局部变量会冲突,建议在函数中只使用本地变量

    [root@centos8 script]$name=hello
    [root@centos8 script]$echo $name
    hello
    [root@centos8 script]$. function 
    [root@centos8 script]$hello
    [root@centos8 script]$echo $name
    tom
    [root@centos8 script]$cat function 
    #!/bin/bash
    function hello(){
       name=tom
    }
    

    在函数中定义本地变量的方法

    local NAME=VALUE
    

    5 其他脚本工具

    5.1 trap

    捕捉系统信号并且执行相应动作

    • trap '触发指令' 信号

      进程收到系统发出的指定信号后,将执行自定义指令,而不会执行原操作

    • trap '' 信号

      忽略信号的操作

    • trap '-' 信号

      恢复原信号的操作

    • trap -p

      列出自定义信号操作

    • trap finish EXIT

      当脚本退出时,执行finish函数

    • signal_spec可以是简写,信号数字、信号全称(int、2、SIGINT)

    范例:

    #捕捉到ctrl+c时什么也不执行,使用kill -L查看信号
    [root@centos8 script]$cat trap.sh 
    #!/bin/bash
    trap '' int quit
    for((i=0;i<=10;i++))
    do
           sleep 1
           echo $i
    done
    

    5.2 mktemp

    创建并显示临时文件或目录,主要用于避免文件命名冲突

    格式:

    mktemp [OPTION]... [TEMPLATE]
    

    注意:TEMPLATE: filenameXXX,X至少要出现三个而且必须是大写

    常见选项:

    • -d 创建目录
    • -p 指定创建临时文件的目录

    范例:

    [root@centos8 test]$mktemp testXXX
    testDji
    
    [root@centos8 test]$mktemp --tmpdir= testXXX
    /tmp/testle5
    
    #rm -rf命令的实现
    [root@centos8 script]$cat rm_rf.sh 
    #!/bin/bash
    DIR=`mktemp -d /tmp/$(date +%F_%H-%M-%S)XXX`
    mv $* $DIR
    echo $* is move to $DIR
    

    5.3 install

    install 功能相当于cp,chmod,chown,chgrp 等相关工具的集合

    格式:

    install [OPTION]... [-T] SOURCE DEST 单文件
    install [OPTION]... SOURCE... DIRECTORY
    install [OPTION]... -t DIRECTORY SOURCE...
    install [OPTION]... -d DIRECTORY...创建空目录
    

    选项:

    • -m MODE.默认755
    • -o OWNER
    • -g GROUP
    • -d DIRNAME 目录

    5.4 expect

    用于实现处理交互命令,实现自动完成交互

    格式:

    expect [选项] [ -c cmds ] [ [ -[f|b] ] cmdfile ] [ args ]
    

    选项:

    • -c:从命令执行expect脚本
    • -d:输出调试信息

    expect中相关命令:

    • spawn 启动新的进程
    • expect 从进程接收字符串
    • send 用于向进程发送字符串
    • interact 允许用户交互
    • exp_continue 匹配多个字符串在执行动作后加此命令
    [root@centos8 script]$expect  -c 'expect "\n" {send "pressed enter\n"}'
    
    pressed enter
    [root@centos8 script]$expect  -dc 'expect "\n" {send "pressed enter\n"}'
    expect version 5.45.4
    expect: does "" (spawn_id exp0) match glob pattern "\n"? no
    
    expect: does "\n" (spawn_id exp0) match glob pattern "\n"? yes
    expect: set expect_out(0,string) "\n"
    expect: set expect_out(spawn_id) "exp0"
    expect: set expect_out(buffer) "\n"
    send: sending "pressed enter\n" to { exp0 pressed enter
    }
    argv[0] = expect  argv[1] = -dc  argv[2] = expect "\n" {send "pressed enter\n"}  
    set argc 0
    set argv0 "expect"
    set argv ""
    

    单分支语法:

    passwdexpect1.5> expect "hi" {send "hello\n"}
    hi
    hello
    
    

    多分支语法:

    #语法1
    [root@centos8 test]#expect
    expect1.1> expect "hi" { send "You said hi\n" } "hehe" { send "Hehe yourself\n"
    } "bye" { send "Good bye\n" }
    hehe
    Hehe yourself
    
    #语法2
    expect {
     "hi" { send "You said hi\n"}
     "hehe" { send "Hehe yourself\n"}
     "bye" { send " Good bye\n"}
    }
    

    定义变量:

    #set	变量名	值
    set ip 10.0.0.7
    

    位置参数:

    #!/usr/bin/expect
    set ip [lindex $argv 0] 
    

    6 数组

    6.1 声明数组

    #普通数组可以不事先声明,直接使用
    declare -a ARRAY_NAME
    #关联数组必须先声明,再使用:关联数组就是自定义索引格式的数组
    declare -A ARRAY_NAME
    
    

    6.2 数组赋值

    1)一次赋值一个元素

    ARRAY_NAME[INDEX]=VALUE
    

    2)一次赋值多个元素

    ARRAY_NAME=(val1 val2 val3)
    

    范例:

    name=("tom" "xiaoming" "xiaohong")
    num=({1..10})
    file=(*.sh)
    

    3)交互式赋值

    read -a ARRAY		
    

    6.3 查看所有数组

    显示所有数组

    declear -a
    

    6.4 引用数组

    引用数组元素

    ${ARRAY-NAME[INDEX]}
    

    范例:

    [root@centos8 script]$declare -a title
    [root@centos8 script]$title=({1..3})
    [root@centos8 script]$echo ${title}
    1
    

    引用数组所有元素

    ${ARRAY_NAME[*]}
    ${ARRAY_NAME[@]}
    

    数组中元素的个数

    ${#ARRAY_NAME[*]}
    ${#ARRAY_NAME[@]}
    

    6.5 删除数组

    删除数组中的某元素

    unset ARRAY[INDEX]
    

    删除整个数组

    unset ARRAY
    

    练习:

    1.分别使用shell和expect实现远程登陆主机

    #expect语法实现(允许用户交互,不会自动退出)
    [root@centos8 script]# cat expect1.sh 
    #!/usr/bin/expect
    set ip [lindex $argv 0]
    set user [lindex $argv 1]
    set passwd [lindex $argv 2]
    spawn ssh $user@$ip
    expect {
       "yes/no" {send "yes\n";exp_continue}
       "password" {send "$passwd\n"}
    
    }
    interact
    
    #shell语法实现(此方式会在到达timeout时间后自动退出,默认timeout时间为10s)
    [root@centos8 script]# cat expect.sh 
    #/bin/bash
    ip=10.0.0.203
    user=root
    passwd=123321
    expect << EOF
    set timeout 2
    spawn ssh $user@$ip
    expect {
    	"yes/no" {send "yes\n";exp_continue}
    	"password" {send "$passwd\n"}
    }
    expect eof
    EOF
    

    2.生成10个随机数保存于数组中,并找出其中最大值和最小值

    [root@centos8 script]$cat random.sh 
    #!/bin/bash
    declare -i max min
    declare -a random
    for((i=0;i<10;i++));do
        random[$i]=$RANDOM
        if [ $i -eq 0 ];then
            min=${random[0]} 
            max=${random[0]}
        else
            [ ${random[$i]} -gt $max ] && max=${random[$i]}
            [ ${random[$i]} -lt $min ] && min=${random[$i]}
        fi
    done
    echo "all num are ${random[*]}"
    echo "max num is $max"
    echo "min num is $min"
    

    3.输入若干个数存入数组中,采用冒泡算法进行排序

    [root@centos8 script]$cat sort.sh 
    #!/bin/bash
    declare -a num
    num=(1 10 6 5 8 7 9 4 3 2)
    echo 调换顺序之前数组顺序 ${num[*]}
    for((j=9;j>0;j--));do
        for ((i=0;i<j;i++));do
           if [ ${num[$i]} -gt ${num[$[i+1]]} ];then
             b=${num[$i]} 
            num[$i]=${num[$[i+1]]}
            num[$[i+1]]=$b
           fi
        done
    done
    echo 调换顺序之后数组顺序 ${num[*]}
    
  • 相关阅读:
    python之内置函数
    装饰器原理剖析
    迭代器&生成器&yield异步
    python之文件操作read
    函数的创建,调用,返回值和传参的讲解
    collections之deque【双向队列】与Queue【单向队列】
    collections之命名元组
    collections系列之OrderedDict【有序字典】与DefaultDict【默认字典】
    collections系列之Counter
    对象之int介绍
  • 原文地址:https://www.cnblogs.com/bestvae/p/14268867.html
Copyright © 2011-2022 走看看