bash脚本编程(3) 变量: 特性:一个变量中仅能存储一个数值 数组:能够容纳多个数组元素的连续的内存空间; 稀疏数组:可间隔取值 稠密数组:需连续取值
数组元素:数组中任何一个存放数据的存储单元,其作用相当一个变量;
数组元素的标识: 索引数组标识:所有的数组元素都是使用数字编号的; 通常数字编号是从0开始的,即:0/1/2…… 关联数组标识:所有的数组元素都可以使用名称(字符串)来标识; 注意:bash4.0以上的版本才有可能支持关联数组;
数组的声明和定义: 1.declare命令: -a to make NAMEs indexed arrays (if supported)
如果支持的话将其后的变量名称声明为索引数组
-A to make NAMEs associative arrays (if supported) 如果支持的话将其后的变量名称声明为关联数组 示例: 定义稠密数组: # declare -a NAMES=("zhang" "wang" "liu") 定义稀疏数组: # daclare -a NAMES=([0]="zhang" [2]"wang" [5]="liu") 2.直接使用变量赋值的方式: 定义稠密索引数组: ARRAY_NAME=("value1" "value2"……) 定义稀疏索引数组: ARRAY_NAME=([0]="value1" [1]="value2" [4]="value3"……) 定义关联数组: ARRAY_NAME=([Index_name1]="value1" [Index_name2]="value2"……) 3.分别定义数组元素: ARRAY_NAME[0]="value1" ARRAY_NAME[1]="value2" …… ARRAY_NAME[N-1]="value[n-1]"@H_502_10@引用数组元素的方式: ${ARRAY_NAME} 注意:如果在引用数组元素时没有给出具体的索引编号,则默认编号为“0”,即显示第一个数组元素的数值;
引用整个数组中所有元素: ${ARRAY_NAME[*]} 或者 ${ARRAY_NAME[@]} 例如: [root@localhost ~]# echo ${NAMES[*]} zhang wang liu 引用整个数组的所有元素的索引号: ${!ARRAY_NAME[*]} 或者 ${!ARRAY_NAME[@]} 例如: [root@localhost ~]# echo ${!NAMES[*]} 0 1 2 查看数组中的元素的个数(数组长度): ${#ARRAY_NAME[*]}或者${#ARRAY_NAME[@]} 例如: [root@localhost ~]# echo ${#NAMES[*]} 3 数组切片: ${ARRAY_NAME[*]:offset} //显示数组中包括offset数值所对应表示的位置的元素及其后面所有的元素; 例如: [root@localhost ~]# echo ${NAMES[*]:2} liu ${ARRAY_NAME[*]:offset:number} //显示数组中包括offset数值所对应表示的位置的元素及其后number个元素的值; 例如: [root@localhost ~]# echo ${NAMES[*]:1:1} wang 撤销数组: unset ARRAY_NAME
RANDOM变量: 随机数变量:0-32767,整数值;
从熵池中取随机数; 熵池: /dev/random 两次敲击键盘的时间间隔; 两次IO的时间间隔; /dev/urandom:伪熵池 利用应用程序计算得到的随机数;@H_502_10@bash脚本编程的结构: bash脚本编程语言: 脚本类语言 解释型语言 过程式编程语言 指令+数据
过程式编程语言的结构: 顺序执行结构: 从上到下,自左而右的执行所有的语句(命令) 选择执行结构: 当条件满足或不满足时才会执行对应的语句(命令) 循环执行结构: 重复执行某段语句(命令)
bash脚本编程语言中也具备上述结构: 顺序执行结构:默认 选择执行结构: 根据给定条件的逻辑判断结果或根据某个可选取的取值范围,进而选择某个分支结构中的命令语句予以执行的方式; if: 选择执行结构的标准,根据条件的逻辑判断结果选择执行的语句内容; case: 选择执行结构的标准,根据符合某特定范围的取值标准选择执行的语句内容;
循环执行结构: 对于特定语句内容,重复执行0次,1次或者多次; for:以遍历列表的方式进行循环; while:根据给定条件的逻辑判断结果来进行循环;逻辑判断结果为真才循环否则停止循环; until:根据给定条件的逻辑判断结果来进行循环;逻辑判断结果为假才循环否则停止循环; select:死循环,即没有默认退出条件的循环;利用循环提供一个可选择的列表;@H_502_10@bash脚本执行结构之if选择执行结构: if: if 命令; then 命令; [ elif 命令; then 命令; ]... [ else 命令; ] 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 COMMANS ; then COMMANDS ; else COMMANDS ; fi 注意:是否会执行then或else后面的命令,取决于if后面的命令的执行状态返回值; 1.如果其返回值为真,则执行then后面的命令; 2.如果其返回值为假,则执行else后面的命令; 建议在脚本中的书写格式: if CONDITION ; then STATEMENT ... else STATEMENT ... fi 或 if CONDITION then STATEMENT ... else STATEMENT ...
fiif语句的多分支结构: 注意:是否会执行then或else后面的命令,取决于if后面的命令的执行状态返回值或elif后面的命令的执行状态返回值; 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 ... else STATEMENT ...
fi
注意:if得多分支结构,使用场景不多,而且有些时候,可以使用嵌套的单分支或双分支if结构代替if多分支结构;嵌套的if结构: if CONDITION1 ; then if CONDITION2 ; then if CONDITION3 ; then STATEMENT ... else STATEMENT ... fi else STATEMENT ... fi@H_502_10@示例: 写一个脚本,判断某个用户的默认登录shell是否为/bin/bash; #/bin/bash # USERNAME=$(cut -d : -f 1 /etc/shasow | sort -R | head -1) USERSHELL=$(egrep "^$USERNAME>" /etc/passwd | cut -d : -f 7) if [ "$USERSHELL" == "/bin/bash" ] ; then echo "${USERNAME}'s login shell is /bin/bash." else echo "${USERNAME}'s login shell is ${USERSHELL}" fi
unset USERNAME USERSHELL
bash脚本编程之用户交换使用: 位置参数变量: $0:命令的本身,对于脚本而言,就是该脚本的路径 $1,$2,$3……,$N: 脚本后面通过命令给脚本传递的命令行参数; N>9时,引用该位置 变量时需要加{},即${10}
特殊变量: $@:给出所有位置参数的列表,当使用双引号时,每个参数作为单独的字符串存在 $*:给出所有位置参数的列表,当使用双引号时,整个参数列表被当做一个字符串 $#: 表示除去$0之外,整个命令行中有多少个参数;
read命令: Read a line from the standard input and split it into fileds. 格式:read - read [-a 数组] [-p 提示符] [-t 超时] [name……] -a array:定义索引数组 -p prompt:给用户输出提示信息 -t timeout:用户输入的超时间;超时后read会自动给其赋空值:如果输入时间过长且一直处于等待时间,也可以使用^+c取消命令 name:变量或数组的名称如果省略此内容,bash会将read读到的信息直接保存到一个内置名为REPLAY的变量中
注意: Linux哲学思想之一:尽量不与用户交互
在使用read命令时用-t选项指定与用户交互时间,一旦时间超过了预定时间,脚本中后续的命令内容会自动被执行;因此,我们通常需要判断通过read赋值的变量值是否为空,如果为空我们可能需要为改变量提供一个默认值 read -t VAR1 [ -z $VAR1 ] && VAR1=value1@H_502_10@示例: #!/bin/bash # if [ $# -ne 1 ] ; then echo "Only can provide ONE argument." exit 5 fi
if ! id $1 &> /dev/null ; then useradd $1 echo $1 | passwd --stdin $1 &> /dev/null else echo "$1 exist already." fi [root@localhost bin]# chmod +x useradd.sh [root@localhost bin]# useradd.sh lisi [root@localhost bin]# id lisi uid=1001(lisi) gid=1001(lisi) 组=1001(lisi)
管理用户脚本: 脚本可以接受两个参数,第一个参数为-a或-d,第二个参数为用户名;如果第一个参数是-a,则创建用户,如果第一个参数是-d,则删除用户; #!/bin/bash
if [ $# -ne 2 ] ; then echo "Make sure provide TWO argument." exit 5 fi if [ $1 == '-a' ] ; then if ! id $2 &> /dev/null ; then useradd $1 &> /dev/null echo $2 | passwd --stdin $2 &> /dev/null echo "User $2 created succesfully and password changed to it's username." else echo "$1 exist 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
[root@localhost bin]# chmod +x contral.sh [root@localhost bin]# contral.sh -d lisi User lisi delete finished. [root@localhost bin]# contral.sh -a nihao -a exist already. 改进版:使用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 if [[ $1 =~ ^[[:digit:]]+$ ]] ; then echo "$1 is a pure digit." else echo "$1 is not a digit." fi
[root@localhost bin]# contral3.sh 5.4 5.4 is not a digit. [root@localhost bin]# contral3.sh 6 6 is a pure digit.
循环执行结构: 循环:将某一段代码或命令重复执行0次,1次或多次;
一个好的循环结构,必须要包括两个重要的环节: 1.进入循环的条件: 在符合要求或满足条件时才开始循环; 2.退出循环的条件: 达到某个要求或符合某个条件时结束或终止循环的执行;
for循环:(help查找) 1.遍历列表的循环: for: 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} {FIRST,SECOND,THIRD..,LAST} [root@localhost ~]# echo {a..z} a b c d e f g h i j k l m n o p q r s t u v w x y z [root@localhost ~]# echo {1..10} 1 2 3 4 5 6 7 8 9 10 奇数列表: [root@localhost ~]# echo {1..100..2} 1 3 5 7 9 11 13 15 17 19 21 23 25 27 29 31 33 35 37 39 41 43 45 47 49 51 53 55 57 59 61 63 65 67 69 71 73 75 77 79 81 83 85 87 89 91 93 95 97 99 2)seq命令 seq [OPTION]... LAST seq [OPTION]... FIRST LAST seq [OPTION]... FIRST INCREMENT LAST [root@localhost ~]# seq 5 1 2 3 4 5 [root@localhost ~]# seq 1 5 1 2 3 4 5 [root@localhost ~]# seq 1 4 32 1 5 9 13 17 21 25 29 3.花括号展开: {FIRST..LAST} 4.命令的执行结果: ls /etc grep /PATH/TO/SOMEFILE 5.GLOBBING(通配符) 6.某些特殊变量的值: $*,$@@H_502_10@循环体: 一般来说,循环体中应该包括能够用到VAR_NAME变量的值的命令或命令的组合;如果循环体中命令并没有用到VAR_NAME变量的值的话,列表的元素的个数就是此次for循环的次数;
计算奇数和; 1)#!/bin/bash # for I in {1..100..2} ; do SUM=$[SUM+I]或者 letSUM+=$I done echo "The sumary is : $SUM" 2)#!/bin/bash # for I in $(seq 1 2 100) ; do SUM=$[SUM+I]或者 letSUM+=$I done echo "The sumary is : $SUM"
写一个脚本,能够通过-a或-d选项添加或删除一个或多个用户账户; #!/bin/bash
version: 0.0.3
if [ $# -lt 2 ] ; then echo "Make sure provide more than TWO arguments." exit 5 fi
if [ $1 == '-a' ] ; then shift for I in "$@" ; do if ! id $I &> /dev/null ; then useradd $I &> /dev/null echo $I | passwd --stdin $I &> /dev/null echo "User $I created succesfully and password changed to it's username." else echo "$I exists already." fi done elif [ $1 == '-d' ] ; then shift for J in "$@" ; do if id $J &> /dev/null ; then userdel -r $J &> /dev/null echo "User $J delete finished." else echo "$J does not exist yet." fi done else echo "Usage: $(basename $0) -a UNAME1 [UNAME2 ...] | -d UNAME1 [UNAME2 ...]" exit 6 fi
总结: 1.进入循环条件:LIST中尚有未被取尽的元素; 2.退出循环的条件:LIST中的元素被取尽; 3.for循环几乎不会出现死循环; 4.在执行循环的过程,需要将整个LIST载入内存,因此,对于大列表来说,可能会消耗件多的内存 计算指定数字范围内自然数的和: #!/bin/bash
declare -i SUM=0 read -p "Please input TWO integer: " INT1 INT2 if [[ $INT1 =~ [^[:digit:]] ]] ; then echo "$INT1 must be an integer." exit 5 fi
if [[ $INT2 =~ [^[:digit:]] ]] ; then echo "$INT2 must be an integer." exit 5 fi
if [ $INT1 -gt $INT2 ] ; then for I in $(seq $INT2 $INT1) ; do
SUM=$[SUM+I]
let SUM+=$I@H_502_10@done echo "The summary is: $SUM" else for I in $(seq $INT1 $INT2) ; do
SUM=$[SUM+I]
let SUM+=$I@H_502_10@done echo "The summary is: $SUM" fi
写一个脚本,打印有""组成的倒置的等腰三角形; ********* 1行,0个空白字符,9个 ******* 2行,1个空白字符,7个* ***** 3行,2个空白字符,5个* *** 4行,3个空白字符,3个* * 5行,4个空白字符,1个*
N行,N-1个空白字符,2*(总行数-当前行号)+1 个*@H_502_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 $[I-1]) ; do echo -n " " done for K in $(seq $[2*(LINENUM-I)+1]) ; do echo -n "*" done echo done 打印九九乘法表 第一行:1个 第二行:2个 ... 第九行:9个 1)第一种 #!/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)第二种 #!/bin/bash
for (( I=1 ; I<=9 ; I++ )) ; do for (( J=1 ; J<=I ; J++ )) ; do echo -ne "$J×$I=$[I*J]\t" done echo done
以上两个例子,均使用for循环的嵌套;往往需要两层的循环嵌套才能打印出平面效果;外层的for循环,负责控制行数输出;内层的for循环,负责控制每一行中各个列的输出; 2.通过控制变量实现for循环: for((exp1;exp2;exp3)); do COMMANDS; done 可以在脚本中写成如下格式: for((exp1;exp2;exp3)); do COMMANDS done
exp1:表达式1,为未指定的变量赋初始值; exp2:表达式2,此次循环的退出条件 exp3:表达式3,指定的变量的值的变化规律;@H_502_10@编程思想: 将人类的自然语言转换成程序的代码语言的方式;
不会开发的运维是没有出路的!
回顾: 数组:一维线性数组; bash交互: $1,…… read if for for VAR_NAME in LIST ; do COMMAND done
bash脚本编程(4) case选择分支结构: case: case 词 in [模式 [| 模式]...) 命令 ;;]... esac
在脚本中使用的case的结构: case ${VAR_NAME} in PATTERN1) COMMAND ... ;; PATTERN2) COMMAND ... ;; ... esac
PATTERN(模式)可以是下列几类字符: 1.普通的文本字符 2.Globbing风格的通配符 *:任意长度的任意字符 ?:任意单个字符 []:指定范围内的任意单个字符 [^]:指定范围以外的任意单个字符 3.| 或字符 注意:在工作当中强烈建议不用rm命令,而是使用mv命令!!!! 写一个脚本: 判断用户利用脚本删除某文件,是否执行删除操作; (1)用case进行编写 #!/bin/bash
if [ -e $1 ] ; then //测试用户输入文件路径文件是否存在; echo -e "\033[5;1;31mDanger!\033[1;31mAre you sure to delete it? [yes or no] \033[0m" //文件存在,提醒用户是否删除Danger闪烁警告 read -t 5 CHOICE //5秒后用户不输入任何值,CHOICE被覆空值 [ -z $CHOICE ] && CHOICE=no //判断如果CHOICE为空值,直接将no赋值给CHOICE case $CHOICE in yes) rm -rf $1 ;; //删除文件 no) echo "Right choice." ;; //不删除文件 esac else echo "$1 does not exist." //显示文件不存在 fi (2)用if,elif进行编写 #!/bin/bash
if [ -e $1 ] ; then echo -e "\033[5;1;31mDanger!\033[0m\033[1;31mAre you sure to delete it? [yes or no] \033[0m" read -t 5 CHOICE [ -z $CHOICE ] && CHOICE=no if [ "$CHOICE" == 'yes' ] ; then rm -rf $1 elif [ "$CHOICE" == 'no' ] ; then echo "Right choice." fi else echo "$1 does not exist." fi
if的多分支结构和case的选择分支结构的异同: 相同点: 1.判断的条件为真时,才会执行对应分之中的语句;条件为假,就跳过不执行; 2.都可以设置默认分支语句,即:所有给定的条件的判定结果都为假时才会执行语句;
不同点: 1.if是根据命令的执行状态返回值的真或假来判断是否该执行某个分之中的语句;case是根据变量中保存的值与指定的模式匹配的结果的真或假来判断是否该执行某个分支中的语句; 2.if的每个分之中无需单独的结束标记,case的每个分支都必须以;;结束;
编写管理用户账户的脚本,第四版,利用case语句+for循环,同时接受创建用户和删除用户的操作; #!/bin/bash
if [ $# -lt 1 ] ; then //如果参数少于一个则显示以下帮助信息 echo -e "Usage: $(basename $0) options... USERLIST\n" echo -e " Options: " echo -e " -a,--add: \vAdd some users from USERLIST." echo -e " -d,--delete: \vDelete some users from USERLIST." echo -e " -h,--help: \vPrint help informationn." echo -e " -v,--verbose: \vPrint more informationn about manage users." echo echo -e " USERLIST FORMAT: " echo -e " USERNAME1,USERNAME2,...,USERNAMEN" exit 5 fi
ADDUSER=0 DELUSER=0 //设置开关量 DEBUG=0
for I in $(seq $#) ; do if [ $# -ne 0 ] ;then case $1 in -h|--help) echo -e "Usage: $(basename $0) options... USERLIST\n" echo -e " Options: " echo -e " -a,--add: \vAdd some users from USERLIST" echo -e " -d,--delete: \vDelete some users from USERLIST" echo -e " -h,--help: \vPrint help informationn" echo -e " -v,USERNAMEN" exit 0 ;; -v|--verbose) DEBUG=1 shift //删除执行操作后的DEBUG变量中的值 ;; -a|--add) ADDUSERLIST=$2 ADDUSER=1 //如果有-a选项了开关就打开了 shift 2 //执行完开关×××后的操作后删除$1,$2变量 ;; -d|--delete) DELUSERLIST=$2 DELUSER=1 shift 2 //执行完开关×××后的操作后删除$1,$2变量 ;; *) echo -e "Usage: $(basename $0) options... USERLIST\n" echo -e " Options: " echo -e " -a,USERNAMEN" exit 6 ;; esac fi done
if [ $ADDUSER -eq 1 ] ; then for J in $(echo $ADDUSERLIST | tr ',' ' ') ; do if ! id $J &> /dev/null ; then //测试用户是否存在 useradd $J &> /dev/null echo $J | passwd --stdin $J &> /dev/null [ $DEBUG -eq 1 ] && echo "Create user $J successfully." else echo "$J exist already." fi done fi
if [ $DELUSER -eq 1 ] ; then for J in $(echo $DELUSERLIST | tr ',' ' ') ; do if id $J &> /dev/null ; then userdel -r $J &> /dev/null [ $DEBUG -eq 1 ] && echo "Delete user $J finished." else echo "$J does not exist yet." fi done fi
while循环结构 while: while 命令; do 命令; done
在脚本中可以写成下列结构 while CONDITION ; do COMMANDS done
while循环进入循环的条件:CONDITION条件为真; while循环退出循环的条件:CONDITION条件为假; until循环结构 until: until 命令; do 命令; done
在脚本中可以写成下列结构: until CONDITION ; do COMMANDS done
until循环进入循环的条件:CONDITIONG逻辑判断结果为假; until循环退出循环的条件:CONDITIONG逻辑判断结果为真;
注意: 1.while CONDITION 相当于 until ! CONDITION 2.while和until循环结构中,没有使变量自增或自减的变化方法,因此需要使用语句给出变量的变化方式;
写一个脚本,使用while 或until循环,计算100以内整数和: #!/bin/bash
declare -i I=0 until [ $I -eq 100 ] ; do let I++ let SUM+=$I done
echo $SUM
#!/bin/bash
declare -i I=0 while [ $I -lt 100 ] ; do let I++ let SUM+=$I done
echo $SUM
循环控制语句: continue continue: continue [n] Resumes for,while,or until loops. 提前结束第n层当前循环,直接进入下一轮条件判断,如果条件判断结果仍然满足循环进入条件,则开启下一轮循环; break break: break [n] Exit for,提前结束第n层循环,而且不再继续后续循环; 计算所有奇数的和 #!/bin/bash
declare -i I=0 while [ $I -lt 100 ] ; do if [ $[I%2] -eq 0 ] ;then //取模为0则只执行 let I++ 如果不为0则执行let SUM+=$I let I++ contine let I++ fi let SUM+=$I let I++ done
echo $SUM
while和until的两种特殊循环使用方法: 1.无限循环方法: while true ; do COMMANDS done
until false ; do COMMANDS done
猜数字: #!/bin/bash
NUMBER=$[RANDOM%100+1] while true ; do read -p "Input a number: " INPTNUM if [ $INPTNUM -gt $NUMBER ] ; then echo "Too big" elif [ $INPTNUM -lt $NUMBER ] ; then echo "Too small" else echo "Yes! you WIN. That's $NUMBER." break fi done
#!/bin/bash
NUMBER=$[RANDOM%100+1] until false ; do read -p "Input a number: " INPTNUM if [ $INPTNUM -gt $NUMBER ] ; then echo "Too big" elif [ $INPTNUM -lt $NUMBER ] ; then echo "Too small" else echo "Yes! you WIN. That's $NUMBER." break fi done
注意:在此类循环结构中需适当添加break和continue控制语句,以使得无限循环可控; 2.实现遍历功能的while和until循环结构: while read LINES ; do COMMANDS done < /PATH/FROM/SOMEFILE
until ! rea LINES ; do COMMANDS done < /PATH/FROM/SOMEFILE
注意:在做遍历循环时建议使用for;
select循环结构 select: select NAME [in 词语 ... ;] do 命令; done Sekevt words from a list and execute commands.
select循环也是一种遍历列表的方式创建一个可视化菜单,每个列表都有一个数字编号与之对应,供用户选择使用,而用户只需要选择其编号即可;
select是一种默认为无限循环结构,因此,必须在循环体中为select提供退出循环条件,通常可以使用break或exit命令实现;
通常情况下select循环会和case一起使用,以进行合理取值判断;
在脚本中实现的格式: select VAR_NAME in LIST ; do COMMANDS done
写一个脚本,显示以/bin/bash为默认shell的用户的ID信息; #!/bin/bash
select I in $(awk -F : '//bin/bash$/{print $1}' /etc/passwd) quit ; do case $I in quit) exit ;; *) echo "The UID of $I is $(id -u $I)" ;; esac done
bash脚本编程——函数 f(x)=y f--> function 对于bash来说,函数就是由命令和语句结构构成的能够实现特定功能的集合;
为什么要用函数? 在bash脚本的编写过程中,有可能出现重复且不做任何改变的代码内容,如果这类内容完全依靠原始代码内容来书写的话不利于排错和优化;因此,我们可以选择将此类代码封装在函数中,在适当的场景中可以重复调用执行;
funcion--> 函数
注意: 1.想要使用函数,必须在使用前定义出来; 2.如果在某个bash脚本中包含了函数体,默认函数体中的各命令和语句是不会执行的;只有在调用函数名的时候,才会执行函数挺重的命令和语句; 3.通常需要重复执行的代码块或命令集,可以封装成函数; 4.被调用的函数只能在调用函数的shell中被执行;
函数名:调用函数时所使用的字符串标识;在一个执行环境中,函数名不允许重复定义;
函数体:能够实现特定独立功能的shell命令或结构化语句块;
定义的语法: 语法一: function func_name { fun_body }
语法二: func_name() { func_body }
注意:在语法二的格式中,func_name和()之间绝对不能有空白字符存在;
注意:函数可以在脚本中被定义,也可以在当前shell中通过交互式环境定义;
函数的使用: 函数在定义的时候,其函数体中包含的所有命令或结构语句都不会被执行;只有在函数被调用时,才能执行其函数体中的各命令和语句;
调用方式:在命令行的脚本中,通过直接给出函数名的方式进行函数调用;
通常我们可以将常用的函数存放在专门用于保存函数的文件中;如果要调用这个文件中已经被定义保存的函数只需要在命令行或脚本中,使用source命令(.命令)加载文件内容到当前shell中,然后再直接使用函数名调用函数即可;
函数的撤销: unset命令 格式:# unset func_name
可以使用set命令查看当前已经定义并生效的函数;
函数的返回值: 两种返回值: 函数的执行结果返回值: 1.在函数体重添加的命令有标准输出; 2.在函数体中使用echo或printf命令强制输出返回信息;
函数的执行结果返回值: 1.默认情况下其状态返回值为函数体中最后一条命令的状态返回值; 2.自定义状态返回值(退出码); return命令 return: return [n] Return from a shell function.
n:0-255(1,2.127位系统保留的状态码,尽量不用) 0:表示无错误返回; 1-255:表示有错误返回; 注意:在函数一旦被调用执行时,一旦遇到return命令,则不会再继续执行函数体中其他的后续命令,立刻结束此次函数的调用执行;@H_502_10@函数的生命周期: 一般来讲,从函数被调用时开始,直到函数体中所有的命令和结构化语句全部执行完成或者遇到return命令,函数的调用结束;
函数的实参: 对应的bash函数来说,没有形参,只有实参; bash函数的实参是使用$1,..位置变量来提供数据的;
func_name pos1 pos2...
可以使用$@或$*表示全部的参数列表; 可以使用$#计算参数的个数;
注意:为函数提供参数时使用的位置变量,是调用函数名时在函数名后面的对应位置上的参数信息;与脚本的未位置参数不是一回事;
变量: 函数被调用时必须是在某特定的shell中被调用,因此,函数中可以继承并识别出环境变量和有调用shell定义的本地变量;
在函数中还可以定义局部变量;而局部变量仅在函数的生命周期内有效,因此,在函数结束只能够行之前我们应给撤销所有该函数定义的局部变量;
局部变量的定义方法: local VAR_NAME=VALUE
变量的替换方式: 前提:定义环境变量: export MYVAR=qhdlink #!/bin/bash
testvar() { local MYVAR=chinalink echo "Internal function: $MYVAR" } echo "Global variables: $MYVAR" MYVAR=link echo "External function,$MYVAR" testvar
函数的递归调用: 广义:在一个函数中调用另一个函数; 下一:在函数体中调用函数自身; 直接调用: func1() { func1 } 间接调用: func2() { func1 }
函数直接递归调用示例1: 计算某个数字的阶乘; 利用for循环: #!/bin/bash
fact=1 if [ $1 -eq 0 ] ; then echo "0! is $fact" elif [ $1 -eq 1 ] ; then echo "1! is $fact" else for I in $(seq $1) ; do let fact=$[fact*$I] done echo "${1}! is $fact" fi
fact(){ if [ $1 -eq 0 ] || [ $1 -eq 1 ] ; then echo 1 else echo "$[$1*$(fact $[$1-1])]" fi }
echo "${1}! is $(fact $1)"
示例2: 斐波那契数列(黄金分割数列)
1 1 2 3 5 8 13 21 34 55 ... N=N-1 + N-2@H_502_10@#!/bin/bash
fabonacci(){ if [ $1 -eq 1 ] || [ $1 -eq 2 ] ; then echo -n "1 " else echo -n "$[$(fabonacci $[$1-1])+$(fabonacci $[$1-2])] " fi }
for I in $(seq $1) ; do fabonacci $I done echo
示例三: 汉诺塔
#!/bin/bash
step=0 move(){ let step++ echo "$step: move disk $1 $2 --> $3" }
hanoi(){ if [ $1 -eq 1 ] ; then move $1 $2 $4 else hanoi "$[$1-1]" $2 $4 $3 move $1 $2 $4 hanoi "$[$1-1]" $3 $2 $4 fi }
hanoi $1 A B C