循环的特殊用法:
1、while循环的特殊用法之遍历文件的每一行
while read line; do 循环体 done < /PATH/FROM/SOMEFILE依次读取/PATH/FROM/SOMEFILE文件中的每一行,将每一行赋值给变量line,从而实现对文件的增删改
练习:扫描/etc/passwd 文件每一行,如发现GECOS 字段为空,则填充用户名和单位电话为62985600 ,并提示该用户的GECOS信息修改成功。
#!/bin/bash #description if user no gecos add something to gecos #version 0.1 #author gaomeng #date 20160819 # while read line ; do gecos=`echo $line | cut -d: -f5` if [ -z $gecos ] &> /dev/null ; then username=`echo $line | cut -d: -f1` usermod -c "$username 62985600" $username echo "$username gecos is changed." fi done < /etc/passwd[root@CentOS6 bin]# getent passwd gao gao:x:500:500::/home/gao:/bin/bash //gecos段无信息 [root@CentOS6 bin]# addgecos.sh abrt gecos is changed. ntp gecos is changed. postfix gecos is changed. gdm gecos is changed. tcpdump gecos is changed. hlr gecos is changed. gao gecos is changed. hadoop gecos is changed. test gecos is changed. [root@CentOS6 bin]# getent passwd gao gao:x:500:500:gao 62985600:/home/gao:/bin/bash //gecos段有信息
2、C语言风格的for循环:
双小括号方法,即((…))格式,代替shell中的in LIST 菜单
单纯用(( ))也可重定义变量值,比如 a=5; ((a++))可将$a重定义为6
for 循环的C语言风格:
for ((控制变量的初始化; 退出此for循环的条件; 控制变量的修正表达式)) do 循环体 done控制变量初始化:仅在运行到循环代码段时执行一次
控制变量的修正表达式:每轮循环结束会先进行控制变量修正运算,而后再做退出for循环的条件判断,若满足条件则退出循环,若不满足则继续循环
例如:打印99乘法表
#!/bin/bash #description echo 9 * 9 #version 0.1 #author gaomeng #date 20160819 # for (( i=1 ; i<=9 ; i++ )) do for (( j=1 ; j<=i ; j++ )) do echo -en "$j*$i=$[$i*$j]\t" done echo done[root@CentOS6 bin]# bash for99.sh 1*1=1 1*2=2 2*2=4 1*3=3 2*3=6 3*3=9 1*4=4 2*4=8 3*4=12 4*4=16 1*5=5 2*5=10 3*5=15 4*5=20 5*5=25 1*6=6 2*6=12 3*6=18 4*6=24 5*6=30 6*6=36 1*7=7 2*7=14 3*7=21 4*7=28 5*7=35 6*7=42 7*7=49 1*8=8 2*8=16 3*8=24 4*8=32 5*8=40 6*8=48 7*8=56 8*8=64 1*9=9 2*9=18 3*9=27 4*9=36 5*9=45 6*9=54 7*9=63 8*9=72 9*9=81
3、select循环与菜单
select variable in list do 循环体命令 donea.select循环主要用于创建菜单,按数字顺序排列的示菜单项将显示在标准错误上,并显示PS3提示符,等待用户输入,用户也可在脚本中修改PS3提示符,使之更加人性化
b.用户输入菜单列表中的某个数字,执行数字后所相应的命令
c.用户输入被保存在内置变量REPLY中,可以在语句中打印此变量
d.select是个无限循环,因此要记住用break命令退出循环,或用exit命令终止脚本。也可以按 ctrl+c退出循环。
e.select经常和case联合使用
f.与for循环类似,可以省略 in list,此时使用位置参量
例如:1.没有修改PS3提示符,也没有配合case语句使用
#!/bin/bash #description show select #version 0.1 #authre gaomeng #date 20160819 # select list in gongbaojiding hongshaorou huobaofeichang shaopaigu do echo $list done[root@CentOS6 bin]# select.sh 1) gongbaojiding 2) hongshaorou 3) huobaofeichang 4) shaopaigu #? 1 gongbaojiding #? 2 hongshaorou #? 3 huobaofeichang #? 4 shaopaigu #? 5 #? 6 #? ^C2.修改了PS3,配合使用了case语句:
#!/bin/bash #description show select #version 0.3 #authre gaomeng #date 20160819 # PS3="What do you want:" select list in gongbaojiding hongshaorou huobaofeichang shaopaigu do case $list in gongbaojiding) echo "$list is 20¥" exit;; hongshaorou) echo "$list is 30¥" exit;; huobaofeichang) echo "$list is 25¥" exit;; shaopaigu) echo "$list is 40¥" exit;; *) echo "no the list, please choice other." esac done[root@CentOS6 bin]# select.sh 1) gongbaojiding 2) hongshaorou 3) huobaofeichang 4) shaopaigu What do you want:1 gongbaojiding is 20¥ [root@CentOS6 bin]# select.sh 1) gongbaojiding 2) hongshaorou 3) huobaofeichang 4) shaopaigu What do you want:4 shaopaigu is 40¥ [root@CentOS6 bin]# select.sh 1) gongbaojiding 2) hongshaorou 3) huobaofeichang 4) shaopaigu What do you want:8 no the list, please choice other. What do you want:3 huobaofeichang is 25¥
函数:
函数function是由若干条shell命令组成的语句块,实现代码重用和模块化编程,即有个功能或模块,我们在代码中会反复使用,则可以写成函数,使用是调用函数即可,而不用多次编写代码
它与shell程序形式上是相似的,不同的是它不是一个单独的进程,不能独立运行,而是shell程序的一部分
函数和shell程序比较相似,区别在于:
Shell程序在子Shell中运行
而Shell函数在当前Shell中运行。因此在当前Shell中,函数可以对shell中变量进行修改
函数的定义:
函数在使用前必须定义,因此应将函数定义放在脚本开始部分,直至shell调用它后才能使用
定义:
函数由两部分组成:函数名和函数体。
语法一:
function f_name {
… 函数体…
}
语法二:
function f_name () {
… 函数体…
}
语法三:
f_name() {
… 函数体…
}
a.可在交互式环境下定义函数
b.可将函数放在脚本文件中作为它的一部分
c.可放在只包含函数的单独文件中,在脚本中用source 和. 调用函数
d.可以将经常使用的函数存入函数文件,然后将函数文件载入shell
e.文件名可任意选取,但最好与相关任务有某种联系。例如:functions.main
f.一旦函数文件载入shell,就可以在命令行或脚本中调用函数。可以使用set命令查看所有定义的函数,其输出列表包括已经载入shell的所有函数。
g.若要改动函数,首先用unset命令从shell中删除函数。改动完毕后,再重新载入此文件。
载入函数:
shell脚本中调用一个已创建好函数文件:
. filename 或 source filename
注意:此处<点> <空格> <文件名>
这里的文件名要使用路径,绝对路径或者相对路径都可以
调用函数:
函数只有被调用才会执行;
调用函数仅使用其函数名即可
函数名出现的地方,会被自动替换为函数代码
函数的生命周期:被调用时创建,返回时终止
删除函数:
使用unset命令完成此功能.
unset function_name
函数的返回值:
函数的执行结果返回值:
(1) 使用echo或printf命令进行输出
(2) 函数体中调用命令的输出结果
函数的退出状态码:
(1) 默认取决于函数中执行的最后一条命令的退出状态码
(2) 自定义退出状态码,其格式为:
return 从函数中返回,用最后状态命令决定返回值
return 0 无错误返回。
return 1-255 有错误
函数参数:
函数可以接受参数:
传递参数给函数:调用函数时,在函数名后面以空白分隔给定参数列表即可;例如“testfunc arg1 arg2 …”
在函数体中当中,可使用$1, $2, … 调用这些参数;还可以使用$@, $*, $# 等特殊变量
注意:请区分函数中的$1和脚本的$1不同之处
例如:如下脚本
#!/bin/bash #description test function $1 $2 #version 0.1 #author gaomeng #date 20160819 # prin() { echo "prin function \$1 is $1." echo "prin function \$2 is $2." } echo "\$1 is $1." echo "\$2 is $2." prin $2 $1[root@CentOS6 bin]# functiontest.sh 3 9 $1 is 3. $2 is 9. prin function $1 is 9. prin function $2 is 3.
函数变量:
变量作用域:
a.环境变量:当前shell和子shell有效
b.本地变量:只在当前shell 进程有效,为执行脚本会启动专用子shell进程;因此,本地变量的作用范围是当前shell脚本程序文件,包括脚本中的函数。
c.局部变量:函数的生命周期;函数结束时变量被自动销毁
注意:如果函数中有局部变量,如果其名称同本地变量,使用局部变量。
在函数中定义局部变量的方法
local NAME=VALUE
#!/bin/bash #description #version 0.1 #author gaomeng #date 20160819 # test1() { echo "test1 name=$name" //没有定义变量 } test2() { local name=haha //定义了局部变量 echo "test2 name=$name" } test3() { name=haha //直接定义了一个变量,会影响脚本中的变量值? echo "test3 name=$name" } name=gaomeng echo "name=$name" test1 echo "name=$name" test2 echo "name=$name" test3 echo "name=$name"[root@CentOS6 bin]# bash functiontest1.sh name=gaomeng test1 name=gaomeng //函数中没有定义变量,则自动使用脚本中的同名变量 name=gaomeng test2 name=haha name=gaomeng //在函数中定义的局部变量没有影响脚本中的变量 test3 name=haha name=haha //在函数中定义的变量影响了脚本中的变量
函数递归:
函数直接或间接调用自身
注意递归层数
递归实例:
阶乘是基斯顿·于 卡曼于 1808 年发明的运算符号,是数学术语一个正整数的阶乘(factorial )是所有小于及等于该数的正整数的积,并且有0 的阶乘为1 。自然数n 的阶乘写作n!
n!=1 ×2 ×3 ×… ×n
阶乘亦可以递归方式定义:0!=1 ,n!=(n-1)! ×n
n!=n(n-1)(n-2)…1
n(n-1)! = n(n-1)(n-2)!
例:fact.sh
#!/bin/bash # fact() { if [ $1 -eq 0 -o $1 -eq 1 ]; then echo 1 else echo $[$1*$(fact $[$1-1])] fi } fact 5
练习:1、写一个函数实现两个数字做为参数,返回最大值
#!/bin/bash #description input two number, and retuen max number. #version 0.1 #author gaomeng #date 20160818 # max(){ if [ $1 -gt $2 ]; then echo "max number is: $1" else echo "max number is: $2" fi } read -p "Input first number: " num1 until echo $num1 | grep -qE "^[0-9]+$";do read -p "your number error, Input first number: " num1 done read -p "Input second number: " num2 until echo $num2 | grep -qE "^[0-9]+$";do read -p "your number error, Input second number: " num2 done max $num1 $num2
[root@CentOS6 bin]# function3.sh Input first number: sdf your number error, Input first number: 123 Input second number: -124 your number error, Input second number: sdkf your number error, Input second number: 100 max number is: 1232、写一个函数实现数字的加减乘除运算,例如输入 1 + 2 ,,将得出正确结果
#!/bin/bash #desription four arithmetic operation #version 0.1 #author gaomeng #date 20160818 # operation() { if [ $2 == "+" ]; then echo "$1+$3=$[$1+$3]" elif [ $2 == "-" ]; then echo "$1-$3=$[$1-$3]" elif [ $2 == "x" ]; then echo "${1}x${3}=$[$1*$3]" elif [ $2 == "/" ]; then echo "$1/$3=$[$1/$3]" else echo "this is error symbol." fi } read -p "Input first number: " num1 until echo $num1 | grep -qE "^\-?[0-9]+$";do read -p "your number error, Input first number: " num1 done i=0 until [ $i -eq 1 ];do read -p "Input operation symbol( please x instead of * ): " num2 [ $num2 == "+" -o $num2 == "-" -o $num2 == "x" -o $num2 == "/" ] &> /dev/null &&i=1 done read -p "Input second number: " num3 until echo $num3 | grep -qE "^\-?[0-9]+$";do read -p "your number error, Input second number: " num3 done operation $num1 $num2 $num3[root@CentOS6 bin]# function4.sh Input first number: sdf your number error, Input first number: 5 Input operation symbol( please x instead of * ): * Input operation symbol( please x instead of * ): x Input second number: -4 5x-4=-203、斐波那契数列又称黄金分割数列,因数学家列昂纳多·斐波那契以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:0 、1 、1 、2 、3 、5 、8 、13 、21 、34 、…… ,斐波纳契数列以如下被以递归的方法定义:F (0 )=0 ,F (1 )=1 ,F (n )=F(n-1)+F(n-2) (n≥2) )
写一个函数,求n 阶斐波那契数列
0 1 2 3 4 5 6 7 8 9
0、1 、1 、2 、3 、5 、8 、13 、21 、34
#!/bin/bash #description series of rabbit #version 0.3 #author gaomeng #date 20160818 # series() { if [ $1 -eq 0 ]; then sum=0 elif [ $1 -eq 1 ];then sum=1 else let sum=`series $[$1-1]`+`series $[$1-2]` fi echo "$sum" } read -p "Please input month number: " num until echo $num | grep -qE "^[0-9]+$";do read -p "your number error, Input month number: " num done series $num[root@CentOS6 bin]# function5.sh Please input month number: -123 your number error, Input month number: sdf your number error, Input month number: 8 21 [root@CentOS6 bin]# function5.sh Please input month number: 1.02323 your number error, Input month number: 10 554、汉诺塔(又称河内塔)问题是源于印度一个古老传说。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。
利用函数,实现N片盘的汉诺塔的移动步骤
#!/bin/bash #description hanoi game #version 0.2 #author gaomeng #date 20160818 # i=1 move() { echo -e "$i)\t$1 plate from $2 to $3." let i++ } 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 } read -p "Please input move plate numbers: " num until echo $num | grep -qE "^[0-9]+$";do read -p "your number error, Input move plate number: " num done hanoi $num A B C[root@CentOS6 bin]# function6.sh Please input move plate numbers: 3 1)1 plate from A to C. 2)2 plate from A to B. 3)1 plate from C to B. 4)3 plate from A to C. 5)1 plate from B to A. 6)2 plate from B to C. 7)1 plate from A to C. [root@CentOS6 bin]# function6.sh Please input move plate numbers: 4 1)1 plate from A to B. 2)2 plate from A to C. 3)1 plate from B to C. 4)3 plate from A to B. 5)1 plate from C to A. 6)2 plate from C to B. 7)1 plate from A to B. 8)4 plate from A to C. 9)1 plate from B to C. 10)2 plate from B to A. 11)1 plate from C to A. 12)3 plate from B to C. 13)1 plate from A to B. 14)2 plate from A to C. 15)1 plate from B to C.
原创文章,作者:megedugao,如若转载,请注明出处:http://www.178linux.com/37631