bash脚本编程之判断和循环
变量: 特性:一个变量中只能存储一个数值; 数组:能够容纳多个数组元素的连续的内存空间; 1)稀疏数组: 2)稠密数组: 数组元素:数组中任何一个存放数据的存储单元,其作用相当于一个变量; 数组元素的标识: 索引数组标识:所有的数组元素都是使用数字编号的; 通常数字标号是从0开始的,即:0,1,2.... 关联数组标识:所有的数组元素都可以使用名称(字符串)来标识; 注意:bash4.0以上版本才有可能支持关联数组; 数组声明定义: 1.declare命令: -a to make NAMEs indexed arrays (if supported) 将其后的变量名称声明为索引数组;declare -a names=([0]='z'[1]='w' [2]='1') -A to make NAMEs associative arrays (if supported) 将其后的变量名称声明为关联数组;declare -a names=( "z" "w" "l" ) 2.直接使用变量赋值的方式: 定义稠密的索引数组 ARRAY-NAME=(“VALUE1” “VALUE2”....) 定义稀疏数组: ARRAY-NAME=([0]=“VALUE1” [1]=“VALUE2”....) 定义关联数组: ARRAY-NAME=(INDEX_name1='VALUE1' INDEX_VALUE2='VALUE2'....) 3.分别定义数组元素: ARRAY_NAME[0]='VALUE' ARRAY_NAME[1]='VALUE' ARRAY_NAME[2]='VALUE' ....... 引用数组元素的方式: ${ARRAY_NAME[INDEX]} 注意:在引用数组元素时,没人给出索引号,默认编号为”0“,即显示第一个数组元素的数值; 引用整个数组中所有元素: ${ARRAY_NAME[*]}或 ${ARRAY_NAME[@]} 引用整个数组的所有元素的索引号: ${!ARRAY_NAME[*]}或 ${!ARRAY_NAME[@]} 查看数组中的元素个数(数组长度): ${#ARRAY_NAME[*]}或 ${#ARRAY_NAME[@]} 数组切片: ${ARRAY_NAME[*]:offset} //显示包括offste数值所对表示的位置的元素极其后所有的元素; ${ARRAY_NAME[*]:offset:number} //显示数组中包括offset数值所对应表示的位置的元素及其后number个元素; 数组撤销: unset ARRAY_NAME RANDOM变量: 随机数变量:0-32767,整数值; 从熵池中取随机数; 熵池: /dev/random 两次敲击键盘的时间间隔; 两次IO的时间间隔; ... /dev/urandom:伪熵池 利用应用程序计算得到的随机数; [root@localhost ~]# echo $RANDOM 25570 [root@localhost ~]# echo $RANDOM 8882 [root@localhost ~]# echo $RANDOM 2373 [root@localhost ~]# echo $RANDOM 4870 [root@localhost ~]# echo $RANDOM 3895 [root@localhost ~]# echo $RANDOM 8353 [root@localhost ~]# echo $RANDOM 14099 bash脚本编程的结构: bash脚本编程语言: 脚本类语言 解释型语言 过程式编程语言 过程式编程语言结构: 顺序执行结构:默认 从上而下,自左而右地执行所有的语句(命令); 选择执行结构: 当条件满足或不足时,才会执行对应的语句(命令); 循环执行结构: 重复执行某段语句(命令); bash脚本编程语中也具有上述结构; 顺序执行结构:默认 选择执行结构: 根据给定条件逻辑判断结果或根据某个可选取的取值范围,进而选择某个分支结构中的命令语句予以执行的方式; if: 选择执行结构的标准,根据条件的逻辑判断结果选择执行的语句内容; case: 选择执行结构的标准,根据符合某特定范围的取值标准选择执行的语句内容; 循环执行结构: 对于特定语句内容 ,重复执行0,1,多次; for:以遍历列表的方式进行循环; while:根据给定条件的逻辑判断结果进行循环,逻辑判断结果为真,循环,否则,停止循环; until:根据给定条件的逻辑判断结果进行循环,逻辑判断结果为假,循环,否则,停止循环; select:死循环,即没有默认退出条件的循环;利用循环提供一个可选择的列表; bash脚本的执行结构之if选择执行结构: if语句:if - if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else COMMANDS; ] fi if语句的单分支结构: if - if COMMANDS; then COMMANDS; fi 注意:是否会执行then后面的命令,取决于if后面的命令的执行状态返回值: 1.如果其返回值为真,则执行then后面的命令; 2.如果其返回值为假,则不执行then后面的命令; 建议在脚本中的书写格式: if CONDITION ; then statement ... fi 或 if CONDITION then STATEMENT .... fi if语句的双分支结构: if - if COMMANDS; then COMMANDS; else COMMANDS; fi 注意:是否执行then后的命令或else后的命令,取决于if后的命令执行状态的返回值; 1.如果返回值为真,则执行then后的命令; 2.如果为假,则执行else后的命令; if语句的多分支结构: if - if COMMANDS; then COMMANDS; else COMMANDS; fi 注意:是否执行then后的命令或else后的命令,取决于if后的命令执行状态的返回值或else后的命令执行状态返回值; 1.首先判断if后的命令的状态返回值是否为真,为真就执行then后的语句;如果为假,就继续判断第一个elif后的命令 执行状态返回值; 2.第一个elif后的命令执行状态返回值为真,就执行第一个elif语句中then后的命令,否则,就继续判断第二个elif后的 执行状态返回值; 3.以此类推,会判断每个elif后面的命令的执行状态返回值是否为真;如果所有的if和elif后面的命令的执行状态返回值均为假,则执行else后面的语句; 建议在脚本中的书写格式: if CONDITION1 ; then STATEMENT ... elif CONDITION2 ; then STATEMENT ... elif CONDITION3 ; then STATEMENT ... ... else STATEMENT ... fi 或 if CONDITION then STATEMENT ... elif CONDITION2 ; then STATEMENT ... elif CONDITION3 ; then STATEMENT ... ... else STATEMENT ... fi 注意:if的多分支结构,使用场景不多,而且有时候,可以使用嵌套的多分支或双分支if结构代替if多分支结构; 嵌套的if结构 if CONDITION1 ; then if CONDITION2 ; then if CONDITION3 ; then STATEMENT ... else STATEMENT ... else STATEMENT ... fi else STATEMENT ... fi COMMAND1 && COMMAND2 || COMMAND3 示例: 写脚本,判断某个用户的默认登录shell是否为/bin/bash #!/bin/bash y=$(cut -d: -f 1 /etc/shadow | sort -R | head -1) w=$(egrep "^$y\>" /etc/passwd | cut -d: -f 7) if [ "$w"=="/bin/bash" ] ; then echo "${y}'s login shell is /bin/bash." else echo "${y}'s login shell is ${w}" fi unset y w bash脚本编程之用户交互使用: 位置参数变量: $0:命令的本身,对于脚本而言,就是该脚本的路径; $1,$2,....$N:脚本后面通过命令行给脚本传递的命令行参数; N>9时,引用该位置变量时需要加{},即${10} 特殊变量: $@:给出的所有位置的参数的列表,当使用双引号时,每个参数作为单独的字符串存在; $*:给出的所有位置的参数的列表,当使用双引号时,每个参数作为单独的字符串存在; $#:表示除去$0之外,整个命令行中有多少个参数; read命令: read [-ers] [-a 数组] [-d 分隔符] [-i 缓冲区文字] [-n 读取字符数] [-N 读取字符数] [-p 提示符] [-t 超时] [-u 文件描述符] [名称 ...] -a array:定义数组(索引数组); -p prompt:给用户输出提示信息; -t timeout:用户输入的超时时间; name :变量或数组的名称;如果省略此内容,bash会将read读到的信息直接保存到内置的名为REPLAY变量中; 注意: Linux思想之一:尽量不与用户交互; 在使用read命令时,通常会使用-t选项来指定与用户的交互时间,一旦超过预定时间,脚本中后续的命令内容 会自动被执行;因此,通常需要在后面判断通过read赋值的变量值是否为空,如果为空,可能为该变量提供默认值; read -t 5 VAR1 [ -z $VAR1 ] && VAR1=value1 管理用户脚本: 脚本可以接受两个参数,第一个参数为-a或-d,第二个参数为用户名;如果第一个参数是-a,则创建其后面参数命名的用户;如果第一个参数为-d,则删除其后面参 数命名的用户; #!/bin/bash # if [ $# -ne 2 ] ; then echo "Make sure provide TWO arguments." exit 5 fi if [ $1 == '-a' ] ; then if ! id $2 &> /dev/null ; then useradd $2 &> /dev/null echo $2 | passwd --stdin $2 &> /dev/null echo "User $2 created succesfully and password changed to it's username." else echo "$2 exists already." fi elif [ $1 == '-d' ] ; then if id $2 &> /dev/null ; then userdel -r $2 &> /dev/null echo "User $2 delete finished." else echo "$2 does not exist yet." fi else echo "Usage: $(basename $0) -a USERNAME | -d USERNAME" exit 6 fi
改进版:使用read命令; #!/bin/bash
if [ $# -ne 1 ] ; then echo "Make sure provide ONE argument." exit 5 fi
if [ $1 == '-a' ] ; then read -p "Please input a username for creating: " USERNAME if ! id $USERNAME &> /dev/null ; then useradd $USERNAME &> /dev/null echo $USERNAME | passwd --stdin $USERNAME &> /dev/null echo "User $USERNAME created succesfully and password changed to it's username." else echo "$USERNAME exists already." fi elif [ $1 == '-d' ] ; then read -p "Please input a username for deleting: " USERNAME read -t 5 -p "confirm? Input 'yes' to continue: " CHOICE [ -z $CHOICE ] && CHOICE='no'
if [ $CHOICE == 'yes' ] ; then if id $USERNAME &> /dev/null ; then userdel -r $USERNAME &> /dev/null echo "User $USERNAME delete finished." else echo "$USERNAME does not exist yet." fi else echo echo "$USERNAME is not deleted." fi else echo "Usage: $(basename $0) { -a | -d }" exit 6 fi
写脚本解决问题: 1.判断用户通过命令行给的一个参数是否为整数。
#!/bin/bash
if [ $# -ne 1 ] ; then echo "Make sure provide ONE digit." exit 5 fi
if [[ $1 =~ ^[[:digit:]]+$ ]] ; then echo "$1 is a pure digit." else echo "$1 is not a digit." fi
循环执行结构: 循环:将某一段代码或命令重复执行0,1或多次; 一个好的循环结构,必须要包括两个重要的环节: 1.进入循环的条件: 在符合要求或满足条件时才开始循环; 2.退出循环条件: 达到某个要求或符号某个条件时需要结束或终止循环的执行; for循环: 1.遍历列表的循环: for 名称 [in 词语 ... ] ; do 命令; done 为列表中的每个成员执行命令。 建议在脚本中的书写格式: for VAR_NAME in LIST ; do 循环体 done 或 for VAR_NAME in LIST do 循环体 done 注意: VAR_NAME:任意指定的变量名称,变量的值是从LIST中遍历获取的各个元素; LIST:for循环需要遍历的列表,可以通过如下方式生成列表: 1.直接给出列表; 2.纯整数列表: 1):花括号展开: {FIRSTNUM..LASTNUM} 2):seq命令 seq [OPTION]... LAST seq 10 seq [OPTION]... FIRST LAST seq 2 6 seq [OPTION]... FIRST INCREMENT LAST seq 0 2 20 3.花括号展开 {FIRST..LAST} 4.命令的执行结果: ls /etc 5.GLOBBING通配符 6.某些特殊变量的值: $*,$@ 循环体: 一般来说,循环体中应该包括能够用到VAR_NAME变量的值的命令或命令的组合;如果循环体中的命令没有用到 VAR_NAME变量的值的话,列表的元素的个数就是此次for循环的次数; #!/bin/bash for i in {1..50} ; do sum=$[sum+i] done echo "$sum" 例子:给脚本传递三个整数,要求: 1) 如果用户传递过来的不是三个参数,报告正确用法; 2) 如果三个参数中有非纯数字字符串,报告错误并提示用户输入数字; 3) 从三个整数中的选出最大数和最小数,并显示出来; 4) 不能使用sort命令等排序; [root@localhost ~]# vim a.sh #!/bin/bash # if [ $# -ne 3 ] ; then echo " please provide three argument" exit 5 fi if [[ $1 =~ [^[:digit:]] ]]||[[ $2 =~ [^[:digit:]] ]] ||[[ $3 =~ [^[:digit:]] ]] ; then echo "exist error" fi if [ $1 -ge $2 ] ; then if [ $1 -ge $3 ] ; then echo "the largest is $1" fi fi if [ $2 -ge $1 ] ; then if [ $1 -ge $3 ] ; then echo "the largest is $2" fi fi if [ $3 -ge $1 ] ; then if [ $3 -ge $2 ] ; then echo "the largest is $3" fi fi if [ $1 -le $2 ] ; then if [ $1 -le $3 ] ; then echo "the smallest is $1" fi fi if [ $2 -le $1 ] ; then if [ $2 -le $3 ] ; then echo "the smallest is $3" fi fi if [ $3 -le $1 ] ; then if [ $3 -le $2 ] ; then echo "the largest is $3" fi fi 结果: [root@localhost ~]# bash a.sh 12 13 14 the largest is 14 the smallest is 12
例:给脚本传递一个整数,分别计算该整数以内所有偶数之和以及奇数之和。 [root@localhost ~]# vim q.sh #!/bin/bash declare i sum=0 sum1 if [[ $1 =~ [^[:digit:]] ]] ;then echo "$1should be an integer" exit 5 fi if [ "$1" -eq 1 ] ; then echo "奇数和为1" exit 5 fi if [ "$1" -eq 2 ] ; then echo "偶数和为2" exit 5 fi
for i in $(seq 1 2 $1) ; do sum=$[sum+i]
done echo "奇数和为$sum" for i in $(seq 2 2 $1 ) ; do sum1=$[sum1+i] done echo "偶数的和为$sum1"
结果:[root@localhost ~]# bash q.sh 3 奇数和为4 偶数的和为2 [root@localhost ~]# bash q.sh 5 奇数和为9 偶数的和为6
例: 8.利用RANDOM变量随机生成十个数字,显示出这十个数字,并显示出其中的最大值和最小值。
[root@localhost ~]# vim 000
#!/bin/bash
touch ytc for i in $( seq 1 10 ) ; do echo $RANDOM &>> ytc done sort -n ytc| head -1 sort -n ytc| tail -1 rm -r ytc
结果: [root@localhost ~]# bash 000 7119 28639 [root@localhost ~]# bash 000 412 31953
例:给脚本传递一个数字作为行总数,分别打印由*组成的最小锐角朝上和朝下的等腰三角形以及菱形。 角朝下
[root@localhost ~]# vim 9 #!/bin/bash
if [ $# -ne 1 ] ; then echo " usage: $(basename $0) integer" exit 5 fi
if [[ $1 =~ [^[:digit:]] ]] ; then echo " usage: $(basename $0) integer" exit 5 fi
linenum=$1 for i in $(seq $linenum) ; do for j in $(seq $[i-1 ]); do echo -n " " done for k in $(seq $[2*(linenum-i)+1 ]) ; do echo -n "*" done echo done
结果:[root@localhost ~]# bash 9 8
******* ***** *** *
角朝上: [root@localhost ~]# vim 10 #!/bin/bash
if [ $# -ne 1 ] ; then echo "Usage: $(basename $0) INTEGER" exit 5 fi
if [[ $1 =~ [^[:digit:]] ]] ; then echo "Usage: $(basename $0) INTEGER" exit 6 fi
linenum=$1 for i in $( seq $linenum) ; do for j in $( seq $[linenum-i]) ; do echo -n " " done for k in $(seq $[2*i-1]) ; do echo -n "*" done echo done
结果: [root@localhost ~]# bash 10 8 * *** ***** *******
菱形: [root@localhost ~]# vim 11 #!/bin/bash
if [ $# -ne 2 ] ; then echo "Usage: $(basename $0) INTEGER" exit 5 fi
if [[ $1 =~ [^[:digit:]] ]] ; then echo "Usage: $(basename $0) INTEGER" exit 6 fi
linenum=$1 for i in $( seq $linenum) ; do for j in $( seq $[linenum-i]) ; do echo -n " " done for k in $(seq $[2*i-1]) ; do echo -n "*" done echo done
linenum=$2
for i in $(seq $linenum) ; do for j in $(seq $[i ]); do echo -n " " done for k in $(seq $[2*(linenum-i)+1 ]) ; do echo -n "*" done echo done
结果: [root@localhost ~]# bash 11 8 7 * *** ***** *******
******* ***** *** *
例:分别打印顺序和旋转的九九乘法表。 1)顺序的九九乘法表是正常的九九乘法表; [root@localhost ~]# vim 8 #!/bin/bash
for i in {1..9} ; do for j in $(seq $i) ; do echo -ne "$i×$j=$[i*j]\t" done echo done 2)旋转的九九乘法表是第一行是1×1=1 1×2=2 1×3=3 1×4=4 ... 1×9=9; 第二行是2×2=4 2×3=6 2×4=8 ... 2×9=18; ... 第九行是9×9=81; [root@localhost ~]# vim 7 #!/bin/bash for i in {1..9} ; do for j in $(seq $i 9) ; do echo -ne "$i×$j=$[i*j]\t" done echo done 结果: [root@localhost ~]# bash 7
1×1=1 1×2=2 1×3=3 1×4=4 1×5=5 1×6=6 1×7=7 1×8=8 1×9=9 2×2=4 2×3=6 2×4=8 2×5=10 2×6=12 2×7=14 2×8=16 2×9=18 3×3=9 3×4=12 3×5=15 3×6=18 3×7=21 3×8=24 3×9=27 4×4=16 4×5=20 4×6=24 4×7=28 4×8=32 4×9=36 5×5=25 5×6=30 5×7=35 5×8=40 5×9=45 6×6=36 6×7=42 6×8=48 6×9=54 7×7=49 7×8=56 7×9=63 8×8=64 8×9=72 9×9=81
总结: 1.进入循环的条件:LIST中还有未被取尽的元素; 2.退出循环的条件:LIST中的元素被取尽; 3.for循环几乎不会出现死循环; 4.在执行循环的过程中,需要将整个LIST载入内存,因此对于大列表来说,可能会消耗较多的内存及cpu资源;
编程思想: 将人类的自然语言转换成程序的代码语言的方式;
DevOPS 不会开发的运维是没有出路的。
流程图
如果 a大于3, 那么 创建一个用户 fi
原文链接:https://www.f2er.com/bash/389455.html