zoukankan      html  css  js  c++  java
  • 第七章 流程控制for和select语句加函数

    一、流程控制之for循环

    1)语法

    1.语法
    #===========》Shell风格语法
    for 变量名 [ in 取值列表 ]
    do
    循环体
    done
    #===========》C语言风格语法
    for ((初值;条件;步长))
    do
    循环体
    done
    
    2.shell风格的for,常用in列表方式
    for i in 1 2 3
    for i in {1,2,3}
    for i in {1..9}
    for i in {9..1}
    for i in {a..z}
    for i in {A..Z}
    for i in {X..Z}
    for i in $(命令)  # 例如:for i in $(head -10 /etc/passwd);do echo $i|cut -d: -
    f1,2;done
    for i in $(find ...)
    
    3.continue与for
    continue:默认退出本次循环
    break:默认退出本层循环
    

    2)案例

    #案例1:shell风格的for
    for i in {1..10}
    do
      echo $i
    done
    
    #案例2:c语言风格的for
    for ((i=1;i<=10;i++))
    do
      echo $i
    done
    
    #案例3:检查内网存活的ip,使用&符号提升脚本的运行效率
    #!/bin/bash
    for i in {1..254}
    do
     (
      ping -W 1 -c 1 192.168.12.$i &> /dev/null 
      if [ $? -eq 0 ];then
          echo "192.168.12.$i is up"
      else
      	  echo "192.168.12.$i is down"
      fi	  
      ) &
    done
    
    #案例4:编写文件类型测试脚本,支持多个参数
    [root@egon /]# cat file.sh
    #!/bin/bash
    for i in $@
    do
    if [[ -d $i ]];then
     echo "$i is directory."
    elif [[ -b $i ]];then
     echo "$i is block device."
      elif [[ -f $i ]];then
     echo "$i is a regular file."
      else
     echo "unknow."
      fi
    done
    [root@egon /]# chmod +x file.sh
    [root@egon /]#
    [root@egon /]# ./file.sh a.txt /etc /root /abc
    a.txt is a regular file.
    /etc is directory.
    /root is directory.
    unknow.
    
    #案例5:可以直接在命令行编写for循环
    [root@egon /]# for i in {1..10};do [ $i -eq 5 ] && continue || echo $i;done
    [root@egon /]# for i in {1..10};do [ $i -eq 5 ] && break || echo $i;done
    
    #案例6:统计dev下每种文件类型的数量
    [root@db04 /scripts/day07]# vim for6.sh
    #!/bin/bash
    # Author:jh
    # Time:2020-11-24  15:16:16
    
    dir="/dev"
    file=0
    directory=0
    directory=0
    link=0
    other=0
    
    for x in `ls $dir`
    do
        if [ -f $dir/$x ];then
            ((file++))
        elif [ -L $dir/$x ];then
            ((link++))
        elif [ -d $dir/$x ];then
            ((directory++))
        else
            ((other++))
        fi
    done
    
    echo "标准文件有:${file}个"
    echo "软连接文件有:${link}个"
    echo "目录文件有:${directory}个"
    
    [root@db04 /scripts/day07]# sh for6.sh 
    标准文件有:108个
    软连接文件有:8个
    目录文件有:82个
    其他文件有:0个
    
    #案例7:向脚本传递一个用户名,验证这个用户是否存在.
    [root@egon ~]# cat testuser.sh
    #!/bin/bash
    id $1 &> /dev/null
    if [ $? -eq 0 ];then
      echo "用户$1存在"
    else
      echo "用户$1不存在"
    fi
    [root@egon ~]# ./testuser.sh root
    用户root存在
    
    #案例8:添加30个用户,再将它们删除
    [root@db04 /scripts/day07]# vim for7.sh
    #!/bin/bash
    # Author:jh
    # Time:2020-11-24  15:37:28
    for i in {01..30}
    do
        useradd user$i
        echo "user$i创建成功"
    done
    
    for i in {01..30}
    do
        userdel -r user$i
        echo "user$i 删除成功"
    done
    
    #案例9:输入账号信息,输入个数,批量创建用户user01、user02、user03...,密码默认123456
    [root@db04 /scripts/day07]# vim for8.sh
    #!/bin/bash
    # Author:jh
    # Time:2020-11-24  15:48:34
    
    read -p "请输入您的账号名称:" name
    read -p "请输入您要创建的个数:" count
    
    for i in `seq -w $count`
    do  
        echo $name$i
        useradd $name$i &>/dev/null
        echo 123456 |passwd --stdin $name$i &>/dev/null
        id $name$i &>/dev/null
        if [ $? -eq 0 ];then
            echo "$name$i 创建成功"
        else
            echo "$name$i 创建失败"
        fi
    done
    
    #案例10:嵌套多层for循环,结合break与continue,(了解即可)
    #1、使用break:
    break 默认参数是 1
    所以写 break 等于 break 1
    意义:退出当前循环层
    break 2 则向上退出2层循环 当前循环也计算在退出层次里
    # 示例
    for i in {0..3}
    do
      echo -e "第一层循环:loop$i"
      for j in {0..3}
      do
        echo -e "	第二层循环:loop$j"
        for n in {0..3}
        do
          echo -e "		第三层循环:loop$n:$i$j$n"
          if ((n==2));then
            break 3
          fi
        done
      done
    done
    
    #2、使用continue
    continue = continue 1
    在当次循环中忽略continue后续的代码
    就是:立即结束当前循环中的当次循环,而转入当前循环的下一次循环
    continue 2 等同于 break 1
    continue 3 等同于 break 2
    总结:continue n 等同于 break n-1
    for i in {0..3}
    do
      echo -e "第一层循环:loop$i"
      for j in {0..3}
      do
        echo -e "	第二层循环:loop$j"
        for n in {0..3}
        do
          echo -e "		第三层循环:loop$n:$i$j$n"
          if ((n==2));then
            continue 3
          fi
        done
      done
    done
    

    二、流程控制之select语句

    1)语法

    #select表达式是bash的一种扩展应用,擅长于交互式场合。用户可以从一组不同的值中进行选择
    select var in ...
    do
    	...
    	break
    done
    

    2)案例

    #案例一
    [root@db04 /scripts/day07]# vim for4.sh 
    #!/bin/bash
    # Author:jh
    # Time:2020-11-24  10:18:29 
    
    PS3="请输入操作编号:"
    
    select choice in {"退出","取款","转账","查询余额"}
    do
        #echo "用户的选择是$choice"
        case $choice in 
        "退出")
             break
              ;;
        "取款")
            echo "正在提款"
            ;;
        "转账")
            echo "正在转账"
            ;;
        "查询余额")
            echo "正在查询余额"
            ;;
        *)
            echo "指令输入错误,请重新输入"
         esac
     done
    
    [root@db04 /scripts/day07]# sh for4.sh 
    1) 退出
    2) 取款
    3) 转账
    4) 查询余额
    请输入操作编号:2
    正在提款
    请输入操作编号:3
    正在转账
    请输入操作编号:4
    正在查询余额
    请输入操作编号:5
    指令输入错误,请重新输入
    请输入操作编号:1
    
    #案例2:若省略 in list 则select会把 $@ 当做列表项
    [root@egon /]# cat select.sh
    #!/bin/bash
    # Author:jh
    # Time:2020-11-24  10:18:29 
    
    PS3="请输入操作编号:"
    
    select choice 
    do
        #echo "用户的选择是$choice"
        case $choice in 
        "退出")
             break
              ;;
        "取款")
            echo "正在提款"
            ;;
        "转账")
            echo "正在转账"
            ;;
        "查询余额")
            echo "正在查询余额"
            ;;
        *)
            echo "指令输入错误,请重新输入"
      esac
    done
    
    [root@db04 /scripts/day07]# ./for5.sh  退出 取款 转账 查询余额
    1) 退出
    2) 取款
    3) 转账
    4) 查询余额
    请输入操作编号:2
    正在提款
    请输入操作编号:3
    正在转账
    请输入操作编号:4
    正在查询余额
    请输入操作编号:5
    指令输入错误,请重新输入
    请输入操作编号:1
    

    三、函数

    1)函数介绍

    1.什么是函数???
    函数就是用来盛放一组代码的容器,函数内的一组代码完成一个特定的功能,称之为一组代码块,调用函数便可触发函数内代码块的运行,这可以实现代码的复用,所以函数又可以称之为一个工具
    
    2.为何要用函数??
    #1、减少代码冗余
    #2、提升代码的组织结构性、可读性
    #3、增强扩展性
    

    2)函数的基本使用

    具备某一功能的工具=>函数
    事先准备好的工具=>函数的定义
    遇到应用场景,拿来就用=>函数的调用
    所以函数的使用原则:先定义,后调用
    
    #语法:
    [ function ] funname [()]
    {
      命令1;
      命令2;
      命令3;
     ...
     [return int;]
    }
    # 示例1:完整写法
    function 函数名() {
    函数要实现的功能代码
    }
    
    # 示例2:省略关键字(),注意此时不能省略关键字function
    function 函数名 {
    函数要实现的功能代码
    }
    
    # 示例3:省略关键字function
    函数名() {
    函数要实现的功能代码
    }
    

    3)调用函数

    # 语法:
    函数名  # 无参调用
    函数名 参数1 参数2  # 有参调用
    # 示例
    function test1(){
      echo "执行第一个函数"
    }
    function test2 {
      echo "执行第二个函数"
    }
    test3(){
      echo "执行第三个函数"
    }
    # 调用函数:直接引用函数名字即调用函数,会触发函数内代码的运行
    test1
    test2
    test3
    

    4)函数参数

    #如果把函数当成一座工厂,函数的参数就是为工厂运送的原材料
    1.调用函数时可以向其传递参数
    	# 调用函数test1,在其后以空格为分隔符依次罗列参数
    	test1 111 222 333 444 555
    	
    2.在函数体内部,通过 $n 的形式来获取参数的值,例如, $1 表示第一个参数, $2 表示第二个参
    数...当n>=10时,需要使用 ${n} 来获取参数
    [root@egon ~]# cat b.sh
    function test1(){
      echo "...start..."
      echo $1
      echo $2
       echo $3
      echo "...end..."
    }
    test1 111 222 333 444 555  # 为函数体传参
    [root@egon ~]# ./b.sh
    ...start...
    111
    222
    333
    ...end...
    
    ps:在脚本内获取脚本调用者在命令行里为脚本传入的参数,同样使用的是$n,不要搞混
    
    [root@egon ~]# cat b.sh
    function test1(){
      echo "...start..."
      echo "这是函数内:$1"
      echo "这是函数内:$2"
      echo "这是函数内:$3"
      echo "...end..."
    }
    # test1 111 222 333 444 555
    test1
    echo "这是脚本级的参数$1"
    echo "这是脚本级的参数$2"
    echo "这是脚本级的参数$3"
    
    [root@egon ~]# ./b.sh xxx yyy zzz mmm nnn
    ...start...
    这是函数内:
    这是函数内:
    这是函数内:
    ...end...
    这是脚本级的参数xxx
    这是脚本级的参数yyy
    这是脚本级的参数zzz
    [root@egon ~]#
    

    5)温故知新

    参数处理 说明
    $# 传递到脚本或函数的参数个数
    $@ 所有参数,与$*类似
    $* 所有参数
    $$ 当前脚本进程的ID号
    $? 获取上一条命令执行完毕后的退出状态。0表示正确,非0代表错误,如果执行的是函数,那么$?取的是函数体内return后的值

    ps:

    #1、当$*和$@没有被引号引用起来的时候,它们确实没有什么区别,都会把位置参数当成一个个体。
    
    #2、"$*" 会把所有位置参数当成一个整体(或者说当成一个单词),如果没有位置参数,则"$*"为空,如果有两个位置参数并且分隔符为空格时,"$*"相当于"$1 $2"
    
    #3、"$@" 会把所有位置参数当成一个单独的字段,如果没有位置参数,则"$@"展开为空(不是空字符串,而是空列表),如果存在一个位置参数,则"$@"相当于"$1",如果有两个参数,则"$@"相当于"$1""$2"等等
    
    
    示例:
    [root@egon ~]# cat b.sh
    echo "=======函数test1==========="
    function test1(){
      echo "$*"  # 111 222 333 444 555
      echo "$@"  # 111 222 333 444 555
      echo $#  # 5
      echo $$   # 87380
      echo $?   # 0
    }
    test1 111 222 333 444 555
    echo "=======函数test2==========="
    function test2(){
      for i in "$*"  # 注意:$*不加引号结果与$@一模一样
      do
        echo $i
      done
    }
    test2 111 222 333 "444 555"  # 注意"444 555"被引号引成了一个参数
    # 运行结果为:111 222 333 444 555
    echo "=======函数test3==========="
    function test3(){
      for i in "$@"  # 注意:$*不加引号结果与$@一模一样
      do
        echo $i
          done
    }
    test3 111 222 333 "444 555"  # 注意"444 555"被引号引成了一个参数
    # 运行结果为:
    # 111
    # 222
    # 333
    # 444 555
    

    6)函数的返回值

    如果把函数当成一座工厂,函数的返回值就是工厂的产品,在函数内使用return关键字返回值,函数内可以有多个return,但只要执行一个,整个函数就会立刻结束
    
    [root@egon ~]# cat b.sh
    function test1(){
      echo 111
      return
      echo 222
      return
      echo 333
    }
    test1
    [root@egon ~]# chmod +x bbbb.sh
    [root@egon ~]# ./b.sh
    111
    
    需要注意的是shell语言的函数中,通常用return返回函数运行是否成功的状态,0代表成功,非零代表失败,需要用$?获取函数的返回值
    #1、如果函数内没有return,那么将以最后一条命令运行结果(命令运行成功结果为0,否则为非0)作为返回值
    [root@egon ~]# cat b.sh
    function test1(){
      echo 111
      echo 222
      echo 333
      xxx  # 运行该命令出错
    }
    test1
    echo $?
    [root@egon ~]# ./b.sh
    111
    222
    333
    ./b.sh:行5: xxx: 未找到命令
    12
    
    #2、如果函数内有return,那么return后跟的只能是整型值并且范围为0-255,用于标识函数的运行结果是否正确, 与C语言不同,shell语言中 0 代表 true,0 以外的值代表 false
    [root@egon ~]# cat b.sh
    function test1(){
      echo 111
      echo 222
      echo 333
      return 0
    }
    test1
    echo $?  # 用$?获取函数的返回值
    [root@egon ~]# ./b.sh
    111
    222
    333
    0
    

    四、变量的作用域

    Shell 变量的作用域(Scope),就是 Shell 变量的有效范围(可以使用的范围)。
    

    1)局部变量:只能在函数内访问

    使用local关键字定义在函数内的变量属于局部范围,只能在函数内使用,如下所示
    [root@localhost shell]# cat hello.sh
    #!/bin/bash
    # 定义函数
    function test(){
      local x=111
      echo "函数内访问x:$x"
    }
    # 调用函数
    test
    echo "在函数外即全局访问x:$x"  # 无法访问函数内的局部变量
    
    执行结果
    [root@localhost shell]# ./hello.sh
    函数内访问x:111
    在函数外即全局访问x:
    

    2)全局变量:可以在当前shell进程中使用

    所谓全局变量,就是指变量在当前的整个 Shell 进程中都有效。每个 Shell 进程都有自己的作用域,彼此之间互不影响。在 Shell 中定义的变量,默认就都是全局变量。
    [root@localhost shell]# cat hello.sh
    #!/bin/bash
    x=2222
    function test(){
      echo "函数内访问x:$x"
    }
    test
    echo "在函数外即全局访问x:$x"
    
    执行结果
    [root@localhost shell]# ./hello.sh
    函数内访问x:2222
    在函数外即全局访问x:2222
    
    ps:
    #1、在函数内定义的变量,如果没有用local声明,那么默认也是全局变量,shell变量语法的该特性与js的变量是类似的(在js函数内部定义的变量,默认也是全局变量,除非加上关键字var)
    
    [root@localhost shell]# cat hello.sh
    #!/bin/bash
    function test(){
      x=2222  # 全局变量
    }
    test
    echo "在函数外即全局访问x:$x" 
    [root@localhost shell]# ./hello.sh
    在函数外即全局访问x:2222
    
    #2、每执行一个解释器,都会开启一个解释的shell进程,每个shell进程都有自己的作用域彼此互不干扰
    [root@localhost shell]# x=111 # 该变量仅仅只在当前shell进程中有效,对新的shell进
    程无影响
    [root@localhost shell]# echo $x
    111
    [root@localhost shell]# bash # 执行bash解释器,则开启一个新的进程,或者干脆直接打开
    一个新的终端
    [root@localhost shell]# echo $x
    [root@localhost shell]#
    
    #3、需要强调的是:
    全局变量的作用范围是当前的 Shell 进程,而不是当前的 Shell 脚本文件,它们是不同的概念。打开一个 Shell 窗口就创建了一个 Shell 进程,打开多个 Shell 窗口就创建了多个 Shell 进程,每个Shell 进程都是独立的,拥有不同的进程 ID。在一个 Shell 进程中可以使用 source 命令执行多个Shell 脚本文件,此时全局变量在这些脚本文件中都有效。
    
    [root@localhost shell]# echo $x
    [root@localhost shell]# cat hello.sh
    #!/bin/bash
    function test(){
      x=2222  # 全局变量
    }
    test
    [root@localhost shell]# source hello.sh # 在当前shell进程中执行,产生一个全局变
    量x
    [root@localhost shell]# echo $x # 在当前shell进程中访问全局变量x,可以看到
    2222
    [root@localhost shell]#
    [root@localhost shell]#
    [root@localhost shell]# cat aaa.sh
    #!/bin/bash
    echo $x
    [root@localhost shell]# source aaa.sh # 在当前shell进程中访问全局变量x,同样可以看
    到
    2222
    
    #结论:函数test内的全局变量x早已超越了文件,即全局变量是超越文件的,作用范围是整个当前bash进程
    

    3)环境变量:在当前进程的子进程中都可以使用

    全局变量只在当前 Shell 进程中有效,对其它 Shell 进程和子进程都无效。如果使用 export 命令将全局变量导出,那么它就在所有的子进程中也有效了,这称为“环境变量”。
    
    环境变量被创建时所处的 Shell 进程称为父进程,如果在父进程中再创建一个新的进程来执行 Shell 命令,那么这个新的进程被称作 Shell 子进程。当 Shell 子进程产生时,它会继承父进程的环境变量为自己所用,所以说环境变量可从父进程传给子进程。不难理解,环境变量还可以传递给孙进程。
    
    [root@localhost shell]# export y=333 # 爷爷
    [root@localhost shell]# bash # 爸爸
    [root@localhost shell]# echo $y
    333
    [root@localhost shell]# bash # 孙子
    [root@localhost shell]# echo $y
    333
    ps:通过exit命令可以一层一层地退出 Shell。
    
    ps:命令set 和 env
        set:显示所有变量
        env:环境变量
    
    注意:
    	#1、环境变量只能向下传递而不能向上传递,即“传子不传父”。
        #2、两个没有父子关系的 Shell 进程是不能传递环境变量的
          我们一直强调的是环境变量在 Shell 子进程中有效,并没有说它在所有的 Shell 进程中都有       效;如果你通过终端创建了一个新的 Shell 窗口,那它就不是当前 Shell 的子进程,环境变       量对这个新的Shell 进程仍然是无效的。
        
        #3、环境变量也是临时的
        [root@localhost ~]# ps aux |grep bash$ |grep -v grep
         root   123436  0.0  0.1 116356  2960 pts/0   Ss  21:52  0:00 -bash
         root   123492  0.0  0.1 116472  2932 pts/0   S   21:54  0:00 bash
         root   123520  0.0  0.1 116440  2988 pts/0   S   21:54  0:00 bash
         注意:
         # -开头的bash代表是在登录终端登录的顶级shell进程
         # 非-开头的bash代表的是子shell进程
         一旦退出了在终端登录的顶级shell,那么该终端下开启的所有子shell都会被回收,export设置的环境变量随即消失
         
         所以说环境变量也是临时的,如果想设置成永久的,需要将变量写入shell配置文件中才可以,
    Shell 进程每次启动时都会执行配置文件中的代码做一些初始化工作,如果将变量放在配置文件
    中,那么每次启动进程都会定义这个变量。
    

    五、登录shell和非登录shell

    1)bash的类型

    1、登录shell:就是通过输入用户名 密码后 或 su - 获得的shell
    2、非登录shell:则是通过bash命令和脚本开启的shell环境
    

    2)区别和关系

    我们知道在linux里一切皆为文件,同样,shell的属性加载也是写到文件里的
    在登陆时就会加载对应文件的内容来初始化shell环境,
    非登录与登录区别就在于加载的文件不同 从而导致获得的shell环境不同
    我们看看登录shell都加载了那些文件
    --> /etc/profile
    --> /etc/profile.d/*.sh
    --> $HOME/.bash_profile
    --> $HOME/.bashrc
    --> /etc/bashrc
    再看非登录shell加载的文件,非登录shell加载的文件要少很多
    --> $HOME/.bashrc
    --> /etc/bashrc
    --> /etc/profile.d/*.sh
    
    通常,我们会将环境变量设置在 $HOME/.bash_profile 中
    但如果不管哪种登录shell都想使用的变量 可以考虑设置在 $HOME/.bashrc或者/etc/bashrc中,因为它们都属于无论如何都会执行的文件,但是,如果我们真的在这类文件中添加了变量,那么意味着每次执行shell都会重新定义一遍该变量,而定义变量是要耗费内存资源的,这非常不可取,所以我们通常会结合export在/etc/profile文件中声明一个全局变量,这样在每次登录用户时产生的顶级shell里会有一个全局变量,所有的子shell都可以看到了,无需重复定义
    
    [root@localhost ~]# vim /etc/profile
    [root@localhost ~]# head -2 /etc/profile
    # /etc/profile
    name="egon"
    [root@localhost ~]# echo $name
    [root@localhost ~]# source /etc/profile # 可以在当前shell中立即生效,也可以退出后重新
    登录终端
    [root@localhost ~]# echo $name
    egon
    

    六、作业

    1.开发一个计算器程序如下,引入函数减少代码冗余
    [root@db04 /scripts/day07]# cat homework01.sh
    #!/bin/bash
    # Author:jh
    # Time:2020-11-24  19:27:47
    # num:homework01.sh
    # Version: 1.0
    # Discription: To  
    
    date=`date +%F`
    echo " ---------------------- "
    echo "|Auther:jh   $date|"
    echo "|这是一款高大上的计算器|"
    echo " ----------------------"
    
    while true
    do
    read -p "请输入第一个整数:" first_num
        expr $first_num + 0 &>/dev/null
        if [ $? -eq 0 ];then
            break
        else
            echo "必须是整数"
        fi
    done
    
    while true
    do
    read -p "请输入第二个整数:" second_num
        expr $second_num + 0 &>/dev/null
        if [ $? -eq 0 ];then
            break
        else
            echo "必须是整数"
        fi
    done
    
    echo " ------------------------"
    echo "|        1.加法          |"
    echo "|        2.减法          |"
    echo "|        3.乘法          |"
    echo "|        4.除法          |"
    echo " ------------------------"
    read -p "请输入你想执行的算法:" choice
    
    
    case $choice in 
        "1")
            res=`expr $first_num + $second_num`
            echo "$first_num+$second_num=$res"
            ;;
        "2")
            res=`expr $first_num - $second_num`
            echo "$first_num-$second_num=$res"
            ;;
        "3")
            res=`expr $first_num * $second_num`
             echo "$first_num*$second_num=$res"
            ;;
        "4")
            res=`expr $first_num / $second_num`
             echo "$first_num/$second_num=$res"
             ;;
        *)
            echo "未知的操作"
    esac
    
    
  • 相关阅读:
    CDH版本大数据集群下搭建的Hue详细启动步骤(图文详解)
    如何正确且成功破解跨平台数据库管理工具DbVisualizer?(图文详解)
    [转]【HTTP】Fiddler(二)
    [转]jQuery UI Dialog Modal Popup Yes No Confirm example in ASP.Net
    [转]artDialog
    [转]GridView排序——微软提供Sort
    [转]GridView中直接新增行、编辑和删除
    [转]asp.net的ajax以及json
    [转]JQuery Ajax 在asp.net中使用总结
    [转]Jquery easyui开启行编辑模式增删改操作
  • 原文地址:https://www.cnblogs.com/jhno1/p/14095762.html
Copyright © 2011-2022 走看看