linux之基础shell脚本编程4 字符串操作,变量赋值,配置用户环境
本章主要写shell有关函数,数组使用
七 函数
7.1 函数的介绍
函数function是由若干条shell命令组成的语句块,实现代码重用和模块化编程。
它与shell程序形式上是相似的,不同的是它不是一个单独的 进程,不能独立运行,而是shell程序的一部分。
函数和shell程序比较相似,区别在于: Shell程序在子Shell中运行,而Shell函数在当前Shell中运行。因此在当前Shell中,函数可以对shell中变量进行修改
7.2 函数的定义
函数由两部分组成:函数名和函数体。
#语法一:
function f_name {
...函数体...
}
#语法二:
function f_name () {
...函数体...
}
#语法三:
f_name (){
...函数体...
}
7.3 函数的定义和使用:
可在交互式环境下定义函数
可将函数放在脚本文件中作为它的一部分
可放在只包含函数的单独文件中
调用:函数只有被调用才会执行;
调用:给定函数名,函数名出现的地方,会被自动替换为函数代码
函数的生命周期:被调用时创建,返回时终止
函数一直保留到用户从系统退出,或执行了 unset name 命令
7.4 函数有两种返回值
函数的执行结果返回值:
(1) 使用echo或printf命令进行输出
(2) 函数体中调用命令的输出结果
函数的退出状态码:
(1) 默认取决于函数中执行的最后一条命令的退出状态码
(2) 自定义退出状态码,其格式为:
return 从函数中返回,用最后状态命令决定返回值
return 0 无错误返回。
return 1-255 有错误返回
7.5 在脚本中定义函数
函数在使用前必须定义,因此应将函数定义放在脚本开始部分,直至shell首次发现它后才能使用
调用函数仅使用其函数名即可
比如:
#!/bin/bash
hello(){
echo "hello to linux"
}
hello #直接调用函数
7.6 使用函数文件
可以将经常使用的函数存入函数文件,然后将函数文件载入shell。
文件名可任意选取,但最好与相关任务有某种联系。例如:functions.main
一旦函数文件载入shell,就可以在命令行或脚本中调用函数。可以使用set命令查看所有定义的函数,其输出列表包括已经载入shell的所有函数。
若要改动函数,首先用unset命令从shell中删除函数。改动完毕后,再重新载入此文件。
7.6.1 创建函数文件
[root@centos7 test]# vim functions.main
#!/bin/bash
#functions.main
#author nineven
findit(){
if [ $# -lt 1 ] ; then
echo "Usage:findit file"
return 1
fi
find / -name $1 -print
}
7.6.2 载入函数文件
函数文件已创建好后,要将它载入shell
定位函数文件并载入shell的格式: . filename 或 source filename
注意:此即<点> <空格> <文件名> 这里的文件名要带正确路径
比如:上例中的函数,可使用如下命令:
[root@centos7 test]# . ./functions.main
或
[root@centos7 test]# source ./functions.main
7.6.3 检查载入函数
使用set命令检查函数是否已载入
set命令将在shell中显示所有的载入函数
比如:
7.6.4 执行shell函数
要执行函数,简单地键入函数名即可
[root@centos7 test]# findit te.sh
/testdir/shell/test/te.sh
/root/Desktop/tmp/testdir/shell/test/te.sh
/root/te.sh
/tmp/testdir/shell/test/te.sh
[root@centos7 test]#
7.6.5 删除shell函数
删除函数,使其对shell不可用 。使用unset命令完成此功能.
命令格式为: unset function_name
比如: $unset findit 再键入set命令,函数将不再显示
[root@centos7 test]# findit te.sh
/testdir/shell/test/te.sh
/root/Desktop/tmp/testdir/shell/test/te.sh
/root/te.sh
/tmp/testdir/shell/test/te.sh
[root@centos7 test]#
[root@centos7 test]# unset findit
[root@centos7 test]# findit te.sh
bash: findit: command not found...
[root@centos7 test]#
7.7 函数参数
函数可以接受参数,传递参数给函数,调用函数时,在函数名后面以空白分隔给定参数列表即可;例如“func arg1 arg2 ...” 在函数体中当中,可使用$1, $2, ...调用这些参数;还可以使用$@, $*, $#等特殊变量
7.8 函数变量
变量作用域:
环境变量:当前shell和子shell有效
本地变量:只在当前shell进程有效,为执行脚本会启动,专用子shell进程;因此,本地变量的作用范围是当前shell脚本程序文件,包括脚本中的函数
局部变量:函数的生命周期;函数结束时变量被自动销毁
注意:如果函数中有局部变量,如果其名称同本地变量,使用局部变量。
在函数中定义局部变量的方法 local NAME=VALUE
7.9 函数递归:
函数递归:
函数直接或间接调用自身
注意递归层数
递归实例: 阶乘是基斯顿·卡曼于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)!
比如:
#!/bin/bash
fact() {
if [ $1 -eq 0 -o $1 -eq 1 ]; then
echo 1
else
echo $[$1*$(fact $[$1-1])] fi }
fact $1
实战训练9
1、写一个服务脚本/root/bin/testsrv.sh,完成如下要求
(1) 脚本可接受参数:start, stop, restart, status
(2) 如果参数非此四者之一,提示使用格式后报错退出
(3) 如是start:则创建/var/lock/subsys/SCRIPT_NAME, 并显示“启动成功” 考虑:如果事先已经启动过一次,该如何处理?
(4) 如是stop:则删除/var/lock/subsys/SCRIPT_NAME, 并显示“停止完成” 考虑:如果事先已然停止过了,该如何处理?
(5) 如是restart,则先stop, 再start 考虑:如果本来没有start,如何处理?
(6) 如是status, 则如果/var/lock/subsys/SCRIPT_NAME文件存在,则显示 “SCRIPT_NAME is running...” 如果/var/lock/subsys/SCRIPT_NAME文件不存在,则显示“SCRIPT_NAME is stopped...” 其中:SCRIPT_NAME为当前脚本名
(7)在所有模式下禁止启动该服务,可用chkconfig 和 service命令管理
#!/bin/bash
syon(){
if [ -e /var/lock/subsys/$0 ];then
echo 1
else
echo 0
fi
}
starts(){
if [ $(syon) -eq 1 ];then
echo "服务已经启动过了"
else
if touch /var/lock/subsys/$0 &> /dev/null ;then
echo "$1启动成功"
else
echo "$1启动失败"
exit 1
fi
fi
}
stops(){
if [ $(syon) -eq 0 ] ;then
echo "服务没有启动"
else
if rm -f /var/lock/subsys/$0 &>/dev/null ;then
echo "停止成功"
else
echo "停止失败$1"
exit 1
fi
fi
}
restarts(){
stops ",服务无法重新启动"
starts "服务正在重新重启中,"
}
statuss(){
if [ $(syon) -eq 0 ] ;then
echo "服务没有启动"
else
echo "服务已经启动过了"
fi
}
case $1 in
start)
starts
;;
stop)
stops
;;
restart)
restarts
;;
status)
statuss
;;
*)
echo "error ,please Usage: $0 {start|stop|restart|status}"
esac
2、编写一个脚本/root/bin/copycmd.sh
(1) 提示用户输入一个可执行命令名称;
(2) 获取此命令所依赖到的所有库文件列表
(3) 复制命令至某目标目录(例如/mnt/sysroot)下的对应路径下;
如:/bin/bash ==> /mnt/sysroot/bin/bash /usr/bin/passwd ==> /mnt/sysroot/usr/bin/passwd
(4) 复制此命令依赖到的所有库文件至目标目录下的对应路径下:
如:/lib64/ld-linux-x86-64.so.2 ==> /mnt/sysroot/lib64/ldlinux-x86-64.so.2
(5)每次复制完成一个命令后,不要退出,而是提示用户键入新的要复制的命令,并重复完成上述功能;直到用户输入quit退出
#!/bin/bash
flag=1
read -p "please input a path that you want to copy : " mubiaodir
panduanlujing(){
path=$(dirname $mubiaodir$ki)
if [ ! -e $path ];then
mkdir -p $path
fi
cp -f $ki $mubiaodir$ki &> /dev/null || flag=0
}
kuwenjian(){
for ki in $(ldd $(which --skip-alias $cmdname) | sed -rn "s@.*[[:space:]]([/].*)[[:space:]].*@\1@p") $(which --skip-alias $cmdname);do
panduanlujing
done
}
while true ;do
read -p "please input a command name , quit is exit:" cmdname
if [ "$cmdname" == "quit" -o "$cmdname" == "exit" ];then
break
else
if (ldd $(which --skip-alias $cmdname)) &> /dev/null ;then
kuwenjian
[ $flag -eq 1 ] && echo "复制成功" || echo "权限不足,复制失败"
else
echo "错误的命令名称"
fi
fi
done
3、写一个函数实现两个数字做为参数,返回最大值
#!/bin/bash
maxnum(){
expr $1 + $2 &> /dev/null && echo $(( $1 > $2 ? $1 : $2 )) || echo "参数错误"
}
maxnum $1 $2
4、写一个函数实现数字的加减乘除运算,例如输入 1 + 2,,将得出正 确结果
#!/bin/bash
yunsuan(){
case $key in
*)
echo "$a$key$b=$[a $key b]"
;;
-)
echo "$a$key$b=$[a $key b]"
;;
+)
echo "$a$key$b=$[a $key b]"
;;
/)
echo "$a$key$b=$[a $key b]"
;;
esac
}
read -p "please input agr1 [+|-|*|/] agr2 :" a key b
if [ "$key" == "*" ];then
if expr $a \* $b &> /dev/null ;then
yunsuan
else
echo "输入错误"
fi
else
if expr $a $key $b &> /dev/null ;then
yunsuan
else
echo "输入错误"
fi
fi
5、斐波那契数列又称黄金分割数列,因数学家列昂纳多·斐波那契以兔子 繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列: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阶斐波那契数列
#!/bin/bash
fei(){
if [ $1 -eq 0 ] ;then
echo 0
elif [ $1 -eq 1 ];then
echo 1
else
echo $[$(fei $[$1-1]) + $(fei $[$1-2])]
fi
}
if [ $# -eq 0 ] ;then
echo "请传一个个整数作为参数"
else
fei $[$1-1]
fi
#!/bin/bash
a=0
b=1
fei(){
for((i=3;i<=$1;i++)){
let c=a+b,a=b,b=c
echo -n "$c "
}
}
if expr $1 + 1 &> /dev/null ;then
if [ $1 -eq 1 ];then
echo $a
elif [ $1 -eq 2 ];then
echo $a $b
elif [ $1 -ge 3 ];then
echo $a $b `fei $1`
else
echo error
fi
else
echo error
fi
6、汉诺塔(又称河内塔)问题是源于印度一个古老传说。大梵天创造世 界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着 64片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放 在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间 一次只能移动一个圆盘。 利用函数,实现N片盘的汉诺塔的移动步骤
#!/bin/bash
move(){
echo "将第$1个盘子从 $2 --> $3 "
}
han(){
if [ $1 -eq 1 ];then
move 1 $2 $4
else
han $[$1-1] $2 $4 $3
move $1 $2 $4
han $[$1-1] $3 $2 $4
fi
}
read -p "请输入盘数:" n
han $n a b c
八 数组
变量:存储在单个元素的内存空间
属组:存储多个元素的连续的内存空间,相当于多个变量的集合
数组名和索引
索引:编号从0开始,属于数值索引
注意:索引可支持使用自定义格式,而不仅是数值格式,即为关联索引,bash4.0版本之后开始支持
bash的属组支持稀疏格式(索引不连续)
8.1 定义数组
1,声明数组:
declare -a ARRAY_NAME
declare -A ARRAY_NAME:关联数组
2,数组元素的赋值:
(1)一次只赋值一个元素
ARRAY_NAME[INDEX]=VALUE
week[0]="oneday"
week[2]="twoday"
(2)一次性赋值全部元素
ARRAY_NAME=("one" "two" "three")
(3)只赋值特定元素
ARRAY_NAME=([0]="val1" [2]="val2"...)
(4)交互式数组赋值
read -a ARRAY
3,引用数组
引用数组元素: ${ARRAY_NAME[INDEX]} 省略[INDEX]表示引用下标为0的元素
${ARRAY_NAME[*]} #输出数组所有内容
${ARRAY_NAME[@]} #输出数组所有内容
数组的长度(数组中元素的个数)
${#ARRAY_NAME[*]}
${#ARRAY_NAME[@]}
比如:生成10个随机数保存于数组中,并找出其最大值和最小值
#!/bin/bash
declare -a num
let num[0]=min=max=$RANDOM
for i in {1..9};do
num[$i]=$RANDOM
[ ${num[$i]} -gt $max ] && max=${num[$i]}
[ ${num[$i]} -lt $min ] && min=${num[$i]}
done
echo ${num[*]}
echo "max=$max min=$min"
练习: 编写脚本,定义一个数组,数组中的元素是/var/log目录下所有以.log结尾的文件;要统计其下标为偶数的文件中的行数之和
#!/bin/bash
declare -i lines=0
declare -a filename
filename=(/var/log/*.log)
for i in $(seq 0 $[${#filename[*]}-1]);do
if [ $[$i%2] -eq 0 ];then
let lines+=$(cat ${filename[$i]} | wc -l)
fi
done
echo "lines is $lines"
4,数组数据处理
a,引用数组中的元素
所有元素: ${ARRAY_NAME[*]} ${ARRAY_NAME[@]}
数组切片: ${ARRAY_NAME[@]:offset:number}
offset: 要跳过的元素个数
number: 要取出的元素个数
取出偏移量之后的所有元素: ${ARRAY_NAME[@]:offset}
b,向数组中追加元素: ARRAY_NAME[${#ARRAYNEM[*]}]
c,删除数组中的某元素:导致稀疏格式:unset ARRAY_NAME[INDEX]
d,关联数组:
declare -A ARRAY_NAME
ARRAY_NAME=([idx_name1]='val1' [idx_name2]='val2'...)
实战训练10
1,生成10个随机数,采用冒泡算法进行升序或降序排序
#!/bin/bash
declare -i n=10
maopao(){
for i in $(seq 1 $[$n-1]);do
for j in $(seq 0 $[$n-$i-1]);do
if [ ${k[$j+1]} -gt ${k[$j]} ];then
tmp=${k[$j+1]}
k[$j+1]=${k[$j]}
k[$j]=$tmp
fi
done
done
}
declare -a k
for i in $(seq 0 $[$n-1]);do
k[$i]=$RANDOM
done
echo "原来随机数:${k[*]}"
maopao
echo "排序后的数:"${k[*]}
2,手动输入数字排序
#!/bin/bash
echo "please input number and split with space"
read -a k
n=${#k[*]}
for((i=0;i<n;i++)){
for((j=0;j<n-i-1;j++)){
[ ${k[$j]} -gt ${k[$j+1]} ] && { tmp=${k[$j]};k[$j]=${k[$j+1]};k[$j+1]=$tmp; }
}
}
echo ${k[@]}