• Shell基础语法——流程控制、函数


    Shell 流程控制

    选择结构:分别是 if else 语句和 case in 语句。

    if 语句:

    语法1:(if 语句)

    1. if  condition
    2. then
    3.     statement(s)
    4. fi

    condition是判断条件,如果 condition 成立(返回“真”),那么 then 后边的语句将会被执行;如果 condition 不成立(返回“假”),那么不会执行任何语句。

    语法2:(if 语句)

    1. if  condition;  then
    2.     statement(s)
    3. fi

    请注意 condition 后边的分号;,当 if 和 then 位于同一行的时候,这个分号是必须的,否则会有语法错误。

    语法3:(if else 语句)

    1. if  condition
    2. then
    3.    statement1
    4. else
    5.    statement2
    6. fi

    如果 condition 成立,那么 then 后边的 statement1 语句将会被执行;否则,执行 else 后边的 statement2 语句。

    语法4:(if elif else 语句)

    1. if  condition1
    2. then
    3.    statement1
    4. elif condition2
    5. then
    6.     statement2
    7. elif condition3
    8. then
    9.     statement3
    10. ……
    11. else
    12.    statementn
    13. fi

    注意,if 和 elif 后边都得跟着 then。

    case in 语句:

    1. case expression in
    2.     pattern1)
    3.         statement1
    4.         ;;
    5.     pattern2)
    6.         statement2
    7.         ;;
    8.     pattern3)
    9.         statement3
    10.         ;;
    11.     ……
    12.     *)
    13.         statementn
    14. esac

    case、in 和 esac 都是 Shell 关键字,expression 表示表达式,pattern 表示匹配模式。

    • expression 既可以是一个变量、一个数字、一个字符串,还可以是一个数学计算表达式,或者是命令的执行结果,只要能够得到 expression 的值就可以。
    • pattern 可以是一个数字、一个字符串,甚至是一个简单的正则表达式。

    case 会将 expression  的值与 pattern1、pattern2、pattern3 逐个进行匹配:

    • 如果 expression 和某个模式(比如 pattern2)匹配成功,就会执行这模式(比如 pattern2)后面对应的所有语句(该语句可以有一条,也可以有多条),直到遇见双分号;;才停止;然后整个 case 语句就执行完了,程序会跳出整个 case 语句,执行 esac 后面的其它语句。
    • 如果 expression 没有匹配到任何一个模式,那么就执行*)后面的语句(*表示其它所有值),直到遇见双分号;;或者esac才结束。*)相当于多个 if 分支语句中最后的 else 部分。

    case in 和正则表达式

    case in 的 pattern 部分支持简单的正则表达式,具体来说,可以使用以下几种格式:

    格式说明
    *表示任意字符串。
    [abc]表示 a、b、c 三个字符中的任意一个。比如,[15ZH] 表示 1、5、Z、H 四个字符中的任意一个。
    [m-n]表示从 m 到 n 的任意一个字符。比如,[0-9] 表示任意一个数字,[0-9a-zA-Z] 表示字母或数字。
    |表示多重选择,类似逻辑运算中的或运算。比如,abc | xyz 表示匹配字符串 "abc" 或者 "xyz"。

    对*)的几点说明:

    Shell case in 语句中的*)用来“托底”,万一 expression 没有匹配到任何一个模式,*)部分可以做一些“善后”工作,或者给用户一些提示。

    可以没有*)部分。如果 expression 没有匹配到任何一个模式,那么就不执行任何操作。

    除最后一个分支外(这个分支可以是普通分支,也可以是*)分支),其它的每个分支都必须以;;结尾,;;代表一个分支的结束,不写的话会有语法错误。最后一个分支可以写;;,也可以不写,因为无论如何,执行到 esac 都会结束整个 case in 语句。

    最后一个分支*)并不是什么语法规定,它只是一个正则表达式,*表示任意字符串,所以不管 expression 的值是什么,*)总能匹配成功。

    循环结构:

    while 循环:

    1. while condition
    2. do
    3. statements
    4. done

     while 循环的执行流程为:

    • 先对 condition 进行判断,如果该条件成立,就进入循环,执行 while 循环体中的语句,也就是 do 和 done 之间的语句。这样就完成了一次循环。
    • 每一次执行到 done 的时候都会重新判断 condition 是否成立,如果成立,就进入下一次循环,继续执行 do 和 done 之间的语句,如果不成立,就结束整个 while 循环,执行 done 后面的其它 Shell 代码。
    • 如果一开始 condition 就不成立,那么程序就不会进入循环体,do 和 done 之间的语句就没有执行的机会。

    until循环

    unti 循环和 while 循环恰好相反,当判断条件不成立时才进行循环,一旦判断条件成立,就终止循环。 

    1. until condition
    2. do
    3. statements
    4. done

     一般 while 循环优于 until 循环,但在某些时候—也只是极少数情况下,until 循环更加有用。

    for 循环

    语法1:

    1. for var in value_list
    2. do
    3. command1
    4. command2
    5. ...
    6. commandN
    7. done

    当变量值在列表里,for 循环即执行一次所有命令,使用变量名获取列表中的当前取值。in 列表可以包含替换、字符串和文件名。

    in列表是可选的,如果不用它,for循环使用命令行的位置参数。

    对 value_list 的说明

    取值列表 value_list 的形式有多种,你可以直接给出具体的值,也可以给出一个范围,还可以使用命令产生的结果,甚至使用通配符

    • 直接给出具体的值:可以在 in 关键字后面直接给出具体的值,多个值之间以空格分隔 ;

    • 给出一个取值范围:{start..end}  注意中间用两个点号相连,这种形式只支持数字和字母;

    • 使用命令的执行结果:使用反引号``或者$()都可以取得命令的执行结果;

    • 使用 Shell 通配符:一种精简化的正则表达式,通常用来匹配目录或者文件,而不是文本;

    • 使用特殊变量:例如 $#、$*、$@、$?、$$ 等,可省略,省略后效果与$@一样。

    语法2: 

    1. for((exp1; exp2; exp3))
    2. do
    3. statements
    4. done

     for 循环中的三个表达式

    for 循环中的 exp1(初始化语句)、exp2(判断条件)和 exp3(自增或自减)都是可选项,都可以省略(但分号;必须保留)。

    1. 省略 exp1:将exp1移到了 for 循环的外面。
    2. 省略 exp2:可以在循环体内部使用 break 关键字强制结束循环
    3. 省略了 exp3:可在循环体中加入修改变量的语句。

     无限循环

    1. while :
    2. do
    3. command
    4. done
    5. #------------------
    6. while true
    7. do
    8. command
    9. done
    10. #------------------
    11. for (( ; ; ))

     select in循环

    select in 循环用来增强交互性,它可以显示出带编号的菜单,用户输入不同的编号就可以选择不同的菜单,并执行不同的功能。
    select in 是 Shell 独有的一种循环,非常适合终端(Terminal)这样的交互场景

    1. select variable in value_list
    2. do
    3. statements
    4. done

     举例:

    1. #!/bin/bash
    2. echo "What is your favourite OS?"
    3. select name in "Linux" "Windows" "Mac OS" "UNIX" "Android"
    4. do
    5. echo $name
    6. done
    7. echo "You have selected $name"
    8. 运行结果:
    9. What is your favourite OS?
    10. 1) Linux
    11. 2) Windows
    12. 3) Mac OS
    13. 4) UNIX
    14. 5) Android
    15. #? 4↙
    16. You have selected UNIX
    17. #? 1↙
    18. You have selected Linux
    19. #? 9↙
    20. You have selected
    21. #? 2↙
    22. You have selected Windows
    23. #?^D

    #?用来提示用户输入菜单编号;^D表示按下 Ctrl+D 组合键,它的作用是结束 select in 循环。

    运行到 select 语句后,取值列表 value_list 中的内容会以菜单的形式显示出来,用户输入菜单编号,就表示选中了某个值,这个值就会赋给变量 variable,然后再执行循环体中的 statements(do 和 done 之间的部分)。

    每次循环时 select 都会要求用户输入菜单编号,并使用环境变量 PS3 的值作为提示符,PS3 的默认值为#?,修改 PS3 的值就可以修改提示符。

    如果用户输入的菜单编号不在范围之内,例如上面我们输入的 9,那么就会给 variable 赋一个空值;如果用户输入一个空值(什么也不输入,直接回车),会重新显示一遍菜单。

    注意,select 是无限循环(死循环),输入空值,或者输入的值无效,都不会结束循环,只有遇到 break 语句,或者按下 Ctrl+D 组合键才能结束循环。

    跳出循环

    在循环过程中,有时候需要在未达到循环结束条件时强制跳出循环,Shell 使用两个命令来实现该功能:break 和 continue

    在C语言、C++C#PythonJava 等大部分编程语言中,break 和 continue 只能跳出当前层次的循环,内层循环中的 break 和 continue 对外层循环不起作用;但是 Shell 中的 break 和 continue 却能够跳出多层循环,也就是说,内层循环中的 break 和 continue 能够跳出外层循环。

    break 关键字 

    语法:break n

    n 表示跳出循环的层数,如果省略 n,则表示跳出当前的整个循环。break 关键字通常和 if 语句一起使用,即满足条件时便跳出循环。

    例1:如果 break 后面不跟数字的话,表示跳出当前循环,对于有两层嵌套的循环,就得使用两个 break 关键字。

    1. #!/bin/bash
    2. i=0
    3. while ((++i)); do #外层循环
    4. if((i>4)); then
    5. break #跳出外层循环
    6. fi
    7. j=0;
    8. while ((++j)); do #内层循环
    9. if((j>4)); then
    10. break #跳出内层循环
    11. fi
    12. printf "%-4d" $((i*j))
    13. done
    14. printf "\n"
    15. done

    例2:在 break 后面跟一个数字,让它一次性地跳出两层循环

    1. #!/bin/bash
    2. i=0
    3. while ((++i)); do #外层循环
    4. j=0;
    5. while ((++j)); do #内层循环
    6. if((i>4)); then
    7. break 2 #跳出内外两层循环
    8. fi
    9. if((j>4)); then
    10. break #跳出内层循环
    11. fi
    12. printf "%-4d" $((i*j))
    13. done
    14. printf "\n"
    15. done

    修改后的代码将所有 break 都移到了内层循环里面。读者需要重点关注break 2这条语句,它使得程序可以一次性跳出两层循环,也就是先跳出内层循环,再跳出外层循环。

    continue 关键字

    语法:continue n

    n 表示循环的层数:

    • 如果省略 n,则表示 continue 只对当前层次的循环语句有效,遇到 continue 会跳过本次循环,忽略本次循环的剩余代码,直接进入下一次循环。
    • 如果带上 n,比如 n 的值为 2,那么 continue 对内层和外层循环语句都有效,不但内层会跳过本次循环,外层也会跳过本次循环,其效果相当于内层循环和外层循环同时执行了不带 n 的 continue。这么说可能有点难以理解,稍后我们通过代码来演示。

    【实例】使用 continue 跳出多层循环,请看下面的代码: 

    1. #!/bin/bash
    2. for((i=1; i<=5; i++)); do
    3. for((j=1; j<=5; j++)); do
    4. if((i*j==12)); then
    5. continue 2
    6. fi
    7. printf "%d*%d=%-4d" $i $j $((i*j))
    8. done
    9. printf "\n"
    10. done

     从运行结果可以看出,遇到continue 2时,不但跳过了内层 for 循环,也跳过了外层 for 循环。

    Shell函数 

     Shell 函数的本质是一段可以重复使用的脚本代码,这段代码被提前编写好了,放在了指定的位置,使用时直接调取即可。

    Shell 函数定义的语法格式如下:

    1. function name() {
    2.     statements
    3.     [return value]
    4. }

    对各个部分的说明:

    • function是 Shell 中的关键字,专门用来定义函数;
    • name是函数名;
    • statements是函数要执行的代码,也就是一组语句;
    • return value表示函数的返回值,其中 return 是 Shell 关键字,专门用在函数中返回一个值;这一部分可以写也可以不写。

    { }包围的部分称为函数体,调用一个函数,实际上就是执行函数体中的代码。

    简化写法

    1. # 省略function 关键字:
    2. name() {
    3. statements
    4. [return value]
    5. }
    6. # 省略函数名后面的小括号:
    7. function name {
    8. statements
    9. [return value]
    10. }

    函数参数

    Shell 中的函数在定义时不能指明参数,但是在调用时却可以传递参数。

    在Shell中,调用函数时可以向其传递参数。在函数体内部,通过 $n 的形式来获取参数的值,例如,$1表示第一个参数,$2表示第二个参数...  $10 不能获取第十个参数,获取第十个参数需要${10}。当n>=10时,需要使用${n}来获取参数。

    特殊字符用来处理参数:

    参数处理说明
    $#传递到脚本或函数的参数个数
    $*以一个单字符串显示所有向脚本传递的参数
    $$脚本运行的当前进程ID号
    $!后台运行的最后一个进程的ID号
    $@与$*相同,但是使用时加引号,并在引号中返回每个参数。
    $-显示Shell使用的当前选项,与set命令功能相同。
    $?显示最后命令的退出状态。0表示没有错误,其他任何值表明有错误。

    函数调用

    调用 Shell 函数时可以给它传递参数,也可以不传递。如果不传递参数,直接给出函数名字即可:

    name

    如果传递参数,那么多个参数之间以空格分隔:

    name param1 param2 param3

    不管是哪种形式,函数名字后面都不需要带括号。

    Shell 函数在定义时不能指明参数,但是在调用时却可以传递参数,并且给它传递什么参数它就接收什么参数。

  • 相关阅读:
    [补题记录] Codeforces Round 904 (Div. 2)(C)
    【JavaScript进阶之旅 ES6篇 第十章】操作原型的方法、super、4种遍历方式、Symbol
    Git--本地仓库
    【Linux】【网络编程】
    Java Web 7 JavaScript 7.5 BOM
    C语言,记负均正
    Wireshark IP实验—Wireshark Lab: IP v7.0(计算机网络自顶向下第七版)
    批量创建Mongodb账号及关联角色
    (二十四)数据结构-选择排序
    关于在 Notion 中使用 Markdown 语法
  • 原文地址:https://blog.csdn.net/weixin_47203903/article/details/128116459