Shell 流程控制

------ ## 一:条件测试 ### 1. 测试方法 | 方法 | 描述 | | :--------------: | :----------------------------------------------------------: | | test 测试表达式 | 利用test命令进行条件测试表达式,test命令与测试表达式之间至少有一个空格 | | [ 测试表达式 ] | 通过[ ]中括号进行条件测试表达式,[]中括号边界与测试表达式之间至少有一个空格 | | [[ 测试表达式 ]] | 通过[[ ]]双中括号进行条件测试表达式,[[ ]]双中括号与测试表达式之间至少有一个空格 | | ((测试表达式)) | 通过(( ))双小括号进行条件测试表达式,( ))双小括号两端不需要空格,常用于整数对比 | ### 2. 文件测试 **语法** ```shell # test 操作符 文件或目录 # [ 操作符 文件或目录] ``` | 操作符 | 描述 | | ----------- | -------------------------------------------------- | | **-d 文件** | 文件存在且为目录则为真 | | **-f 文件** | 文件存在且为普通文件则为真 | | **-e 文件** | 文件存在则为真,不辩别是目录还是文件 | | **-s 文件** | 文件存在且文件大小不为0则为真 | | **-r 文件** | 文件存在且可读则为真,与执行脚本的用户权限也有关 | | **-w 文件** | 文件存在且可写则为真,与执行脚本的用户权限也有关 | | **-x 文件** | 文件存在且可执行则为真,与执行脚本的用户权限也有关 | | -L 文件 | 文件存在且为链接文件则为真 | | f1 -nt f2 | 文件f1比文件f2新则为真,根据文件的修改时间计算 | | f1 -ot f2 | 文件f1比文件f2旧则为真,根据文件的修改时间计算 | 示例: ```bash [root@wxin ~]# test -d /home [root@wxin ~]# echo $? 0 [root@wxin ~]# test -d /home111 [root@wxin ~]# echo $? 1 [root@wxin ~]# [ ! -d /ccc ] [root@wxin ~]# [ ! -d /ccc ] && mkdir /ccc [root@wxin ~]# [ -d /ccc ] || mkdir /ccc ``` ### 3. 数字比较 **语法** ```shell # test 数字 操作符 数字 # test $((变量)) 操作符 $((变量)) # [ 数字 操作符 数字 ] # [ $((变量)) 操作符 $((变量)) ] # [[ 数字 操作符 数字 ]] # [[ $"变量" 操作符 $"变量" ]] # [[ $((变量)) 操作符 $((变量)) ]] # ((数字 操作符 数字)) # (($"变量" 操作符 $"变量")) ``` | **在[]、[[ ]]和test中使用** | **在(( ))中使用** | **说明** | | --------------------------- | ----------------- | -------------------------------- | | -eq | ==或= | 等于,全拼为equal | | -ne | != | 不等于,全拼为not equal | | -gt | > | 大于,全拼为greater than | | -ge | >= | 大于等于,全拼为greater or equal | | -lt | < | 小于,全拼为less than | | -le | <= | 小于等于,全拼为less or equa | **示例:** ```bash [root@wxin ~]# test 1 -gt 0;echo $? 0 [root@wxin ~]# test $((num1)) -gt $((num2));echo $? 1 [root@wxin ~]# [ 1 -gt 0 ];echo $? 0 [root@wxin ~]# [ $((num1)) -gt $((num2)) ];echo $? 1 [root@wxin ~]# [[ 1 -lt 2 ]];echo $? 0 [root@wxin ~]# [[ $"num1" != $"num2" ]];echo $? 0 [root@wxin ~]# [[ $((num1)) -eq $((num2)) ]];echo $? 1 [root@wxin ~]# ((1 == 2));echo $? 1 [root@wxin ~]# (($"num1"<$"num2"));echo $? 0 ``` ### 4. 字符串比较 语法: ```shell # test 参数 字符串 # test 字符串1 参数 字符串 # [ 参数 字符串 ] # [ 字符串1 参数 字符串2 ] ``` | 参数 | 示例 | 功能 | | --------- | ------ | -------------------------------------------- | | **-z** | s1 | 如果字符串s1的长度为0,则测试条件为真 | | **-n** | s1 | 如果字符串s1的长度大于0,则测试条件为真 | | | s1 | 如果字符串s1不是空字符串,则测试条件为真 | | **=或==** | s1=s2 | 如果s1等于s2,则测试条件为真,“=”前后应有空格 | | **!=** | s1!=s2 | 如果s1不等于s2,则测试条件为真 | | < | s1 | s1>s2 | 如果按自定顺序s1在s2之后,则测试条件为真 | **示例:** ```bash [root@wxin ~]# str="12345" [root@wxin ~]# str2="namespace" [root@wxin ~]# test -z "$str";echo $? 1 [root@wxin ~]# test "$str";echo $? 0 [root@wxin ~]# [ -z "$str" ];echo $? 1 [root@wxin ~]# [ "$str" != "$str2" ];echo $? 0 ``` ### 5. 逻辑操作符 | **在[]和test中使用** | **在[[ ]]和(( ))中使用** | **说明** | | -------------------- | ------------------------ | ---------------------------------- | | -a | && | and,与,两端都为真,则结果为真 | | -o | \|\| | or,或,两端有一个为真,则结果为真 | | ! | ! | not,非,两端相反,则结果为真 | ## 二:条件判断 ### 1. 流程控制 ​ 在一个shell脚本中的命令执行顺序称作脚本的流;大多数脚本会根据一个或多个条件来改变它们的流。 ​ 流控制命令:能让脚本的流根据条件而改变的命令称为条件流控制命令。 ​ exit语句:退出程序的执行,并返回一个返回码,返回码为0正常退出,非0为非正常退出。 ​ 条件判断:if代码返回0表示真,非0为假。 ### 2. 语法结构 **单分支结构** ```shell if [ 条件判断式 ]; then 条件成立时,执行的程序 fi ``` **双分支结构** ```shell if [ 条件判断式 ]; then 条件成立时,执行的程序 else 条件不成立时,执行的另一个程序 fi ``` **多分支结构** ```bash if [ 条件判断式1 ]; then 当条件判断式1成立时,执行程序1 elif [ 条件判断式2 ]; then 当条件判断式2成立时,执行程序2 ...省略更多条件.... else 当所有条件都不成立,最后执行此程序 fi ``` **case** ```shell case $变量名 in 值1) 如果变量的值等于值1则执行指令1 ;; 值2) 如果变量的值等于值2则执行指令2 ;; 值3) 如果变量的值等于值3则执行指令3 ;; *) 如果变量的值不等于以上列出的任何值则执行默认指令 esac ``` ## 三:循环结构 ### 1. for 循环 **语法结构** ```bash for 变量 in 值集合 do 执行命令 done ``` - for 每次从值集合中取一个值赋值给变量 - do - done 将赋值后的变量带入执行的命令得到执行结果 - 重复以上两个步骤,直到值集合中的值被一一获取赋值给变量的到所有结果,循环结束 ### 2. while 循环 **语法结构** ```shell while 条件测试 do 执行命令 done ``` - while 首先进行条件测试,如果传回值为0(条件测试为真),则进入循环,执行命令区域,否则不进入循环。 - 满足 while 测试条件,执行命令区域,直到 while 的测试条件不满足结束执行while循环(如果条件一直满足执行无穷循环)。 ### 3. until 循环 **语法结构** ```shell until 条件测试 do 执行命令 done ``` - until 条件测试结果为假(传回值不为0),就进入循环。 - 条件测试不满足,执行命令区域。直到 until 条件满足,结束执行until 循环(如果条件一直不满足则执行无穷循环)。 ## 四:循环控制 ### 1. break **语法结构** ```shell break n # n 表示跳出循环的次数, 如果省略n表示跳出单个循环 ``` **示例** ```bash for i in {1..3}; do for j in {1..3}; do for k in {1..3}; do if [ $k -eq 2 ]; then break 2 fi echo "i=$i,j=$j,k=$k" done done done 输出: i=1, j=1, k=1 i=2, j=1, k=1 i=3, j=1, k=1 break 2 # 跳出2次循环 for i in {1..3}; do for j in {1..3}; do for k in {1..3}; do if [ $k -eq 2 ]; then break fi echo "i=$i,j=$j,k=$k" done done done 输出: i=1, j=1, k=1 i=1, j=2, k=1 i=1, j=3, k=1 i=2, j=1, k=1 i=2, j=2, k=1 i=2, j=3, k=1 i=3, j=1, k=1 i=3, j=2, k=1 i=3, j=3, k=1 ``` ### 2. continue **语法结构** ```shell continue n # 跳转到第n层封闭循环的下一个迭代 ``` **示例** ```bash for i in {1..3}; do for j in {1..3}; do if [ $j -eq 2]; then continue fi echo "i=$i, j=$j" done done 输出: i=1, j=1 i=1, j=3 i=2, j=1 i=2, j=3 i=3, j=1 i=3, j=3 for i in {1..3}; do for j in {1..3}; do if [ $j -eq 2]; then continue 2 fi echo "i=$i, j=$j" done done 输出: i=1, j=1 i=2, j=1 i=3, j=1 ``` ### 3. exit **语法格式** ```shell exit # 退出循环 ``` **示例:** ```bash [root@wxin ~]# vim exit.sh #!/bin/bash for i in {1..5}; do for j in {1..5}; do if [ $j -eq 3 ]; then exit fi echo "$i,$j" done done [root@wxin ~]# bash exit.sh 1,1 1,2 ``` ## 五:shift 指令 ​ shift 命令用于将参数列表 list 左移指定次数,最左端的那个参数就从列表中删除,其后边的参数继续进入循环。 ​ shift[N]:用于将参量列表 list 左移指定次数,缺省为左移一次。 ​ 参量列表 list 一旦被移动,最左端的那个参数就从列表中删 除。while 循环遍历位置参量列表时,常用到 shift **语法格式** ```shell shift [n] # 默认 n=1,即左移 1 位;n 可以指定移动的位数 ``` **示例** ```bash # vim demo.sh #!/bin/bash while [ $# -gt 0 ] do echo $* shift done # ./demo.sh a b c d e f g h a b c d e f g h b c d e f g h c d e f g h d e f g h e f g h f g h g h h ``` ## 六:格式化打印 ### 1. 语法格式 ```shell printf "格式字符串" 参数1 参数2 ... ``` - **格式字符串**:包含格式说明符(如 `%s`, `%d`, `%f`),用于定义输出样式。 - **参数**:按顺序替换格式字符串中的说明符。 - **自动换行**:`printf` **不会自动换行**,需手动添加 `\n`。 ### 2. 格式说明符 | 说明符 | 用途 | 示例 | | ------ | ------------- | --------------------- | | `%s` | 字符串 | `printf "%s" "Hello"` | | `%d` | 整数 | `printf "%d" 42` | | `%f` | 浮点数 | `printf "%f" 3.14` | | `%%` | 输出 `%` 符号 | `printf "%%"` | | `\n` | 换行 | `printf "%d\n" 42` | | `\f` | 换页 | `printf "%d\f" 42` | | `\r` | 回车 | `printf "%d\r" 42` | ### 3. 格式控制 **字段宽度与对齐** - **右对齐**:默认行为,指定宽度(如 `%10s`)。 - **左对齐**:用 `-` 符号(如 `%-10s`)。 - **填充字符**:默认用空格,可以用 `0` 填充数字(如 `%05d`)。 ```bash printf "右对齐: |%10s|\n" "Text" # | Text| printf "左对齐: |%-10s|\n" "Text" # |Text | printf "零填充: %05d\n" 7 # 00007 ``` **浮点数精度** - 控制小数位数:`%.Nf`(`N` 为保留的小数位数)。 ```bash printf "保留两位小数: %.2f\n" 3.1415 # 3.14 ``` ## 七:颜色 ### 1. 语法格式 使用 `\033[` 或 `\e[` 作为转义序列的起始符,格式为: ```shell echo -e "\033[样式代码;前景色;背景色m文本内容\033[0m" ``` - **`-e` 选项**:允许 `echo` 解析转义字符(必须加上)。 - **`\033[0m`**:重置所有样式(避免后续文本也应用颜色)。 ### 2. 颜色代码表 **文本样式** | 代码 | 样式 | | ---- | -------------------------- | | `0` | 重置所有样式 | | `1` | 粗体 | | `4` | 下划线 | | `7` | 反显(反转前景色和背景色) | **前景色(文字颜色)** | 代码 | 颜色 | 代码(亮色) | | ---- | ------ | ------------ | | `30` | 黑色 | `90` | | `31` | 红色 | `91` | | `32` | 绿色 | `92` | | `33` | 黄色 | `93` | | `34` | 蓝色 | `94` | | `35` | 品红色 | `95` | | `36` | 青色 | `96` | | `37` | 白色 | `97` | **背景色** | 代码 | 颜色 | 代码(亮色) | | ---- | ------ | ------------ | | `40` | 黑色 | `100` | | `41` | 红色 | `101` | | `42` | 绿色 | `102` | | `43` | 黄色 | `103` | | `44` | 蓝色 | `104` | | `45` | 品红色 | `105` | | `46` | 青色 | `106` | | `47` | 白色 | `107` | 示例: ```bash # 红色文字 echo -e "\033[31m这是红色文字\033[0m" # 绿色背景 + 黄色文字 echo -e "\033[42;33m绿色背景上的黄色文字\033[0m" # 粗体 + 蓝色文字 echo -e "\033[1;34m粗体蓝色文字\033[0m" # 下划线 + 亮红色文字 echo -e "\033[4;91m下划线亮红色文字\033[0m" ```