一、流程控制
过程式编程语言:顺序执行、选择执行、循环执行
1.1 条件语句
1.1.1 if条件选择语句
选择执行: (注意: if 语句可嵌套)
v 单分支:if 判断条件;then
条件为真的分支代码
fi
v 双分支:if 判断条件; then
条件为真的分支代码
else
条件为假的分支代码
fi
v 多分支: if 判断 条件 1 ; then
if-true
elif 判断 条件 2 ; then
if-ture
elif 判断 条件 3 ; then
if-ture
…
else
all-false
fi
v 逐条 件进行判断,第一次遇为“真”条件时,执行其分支,而后结束整个if 语句
1.1.2 if 实例
v 根据命令的退出状态来执行命令
if ping -c1 -W2 station1 &> /dev/null; then
echo 'Station1 is UP'
elif grep "station1" ~/maintenance.txt &> /dev/null;then
echo 'Station1 is undergoing maintenance’
else
echo 'Station1 is unexpectedly DOWN!'
exit 1
fi
1.1.3 case条件判断
适合于离散值的匹配;如:value 1,3,5 cmd1;value 2,4,6 cmd2… read p “please input a number ” value
case 变量引用($) in 如:case $value in
PAT1) 1|3|5)
分支1 echo a
;; ;;
PAT2) 2|4|6)
分支2 echo b
;; ;;
…
*) *)
默认分支 echo d
;; ;;
esac esac
case 支持glob 风格的通配符:
*: 任意长度任意字符 ; ?: 任意单个字符 ; [] :指定范围内的任意单个字符 ; a|b: a 或b
1.1.4 练习
1 、编写脚本/root/bin/createuser.sh ,实现如下功能:使用一个用户名做为参数,如果指定参数的用户存在,就显示其存在,否则添加之;显示添加的用户的id 号等信息
read -p "please give me a user:" u
useradd $u &> /dev/null
if [ $? -eq 0 ];then
echo "user $u is already added sucessfully" ; id $u
else
echo "user is exit"
fi
2、编写脚本/root/bin/yesorno.sh ,提示用户输入yes 或no,并判断用户输入的是yes 还是no, 或是其它信息
read -p "please input yes or no:" i
case $i in
[yY]|[yY][eE][sS])
echo yes
;;
[nN]|[nN][oO])
echo no
;;
*)
echo your answer is wrong
;;
esac
3 、编写脚本/root/bin/filetype.sh, 判断用户输入文件路径,显示其文件类型(普通,目录,链接,其它文件类型)
for file in $D
do
if [ -L $file ];then
echo -e "\033[32m$file\033[0m is a link file"
elif [ ! -L $file -a -d $file ];then
echo -e "\033[34m$file\033[0m is a directory"
elif [ ! -L $file -a -f $file ];then
echo -e "\033[37m$file\033[0m is a normal file"
elif [ ! -L $file -a -c $file ];then
echo -e "\033[47;33m$file\033[0m is a character device file"
elif [ ! -L $file -a -b $file ];then
echo -e "\033[33m$file\033[0m is a block device file"
elif [ ! -L $file -a -p $file ];then
echo -e "\033[35m$file\033[0m is a pipe file"
elif [ ! -L $file -a -s $file ];then
echo -e "\033[31m$file\033[0m is a socket file"
else
echo -e "\033[47;30mIt's not a directory\033[0m"
fi
done
4 、编写脚本/root/bin/checkint.sh, 判断用户输入的参数是否为正整数。
read -p "please input a number:" n
[ -z $n ] && echo "please input a int" && exit 123
num=`echo $n | egrep -o "\-?[[:digit:]]+"` #\-? 判断–是否出现\为转义符号
if [ "$num" == "$n" ];then
if [ "$num" -le 0 ];then
echo your input is a negative integer #负整数
elif [ "$num" == 0 ];then
echo your input is zero #0
else
echo your input is positive integer #正整数
fi
else
echo your input is not a integer #不是一个整数
fi
1.2 循环语句
循环执行:将某代码段重复运行多次
重复运行多少次:循环次数事先已知;循环次数事先未知
有进入条件和退出条件:for, while, until
1.2.1 for循环
for 变量名 in 列表;do
循环体
done
执行机制 :
依次将列表中的元素赋值给“变量名”; 每次赋值后即执行一次循环体; 直到列表中的元素耗尽,循环结束
列表生成方式:
(1) 直接给出列表
(2) 整数列表:(a) {start..end} ; (b) $(seq [start [step]] end) 如:seq 1 10 表示1-10列表竖行显示
(适用于数值运算) seq 0 2 10 表示显示0 2 4 6 8 10,2为步长
(3) 返回列表的命令:$(COMMAND)
(4) 使用glob, 如:*.sh
(5) 变量引用;$@, $*
1.2.2 for练习
v 1、判断/var/ 目录下所有文件的类型
v 2、 添加10个用户user1-user10 ,密码为8 位随机字符
read -p "how many users do you want to add: " N
#从1-$N中所有数据添加
for j in $(seq 1 $N)
do
#添加用户,但不显示过程
useradd user$j &> /dev/null
#取随机8位数,作为用户的密码
cat /dev/urandom |tr -dc "[^[:alnum:]]"|head -c 8|passwd user$j &> /dev/null
done
v 3、/etc/rc.d/rc3.d 目录下分别有多个以K 开头和以S 开头的文件 ;分别 读取每个文件,以K 开头的文件输出为文件加stop,以S 开头的文件输出为文件名加start
“K34filename stop” “S66filename start”
for j in $(ls -1 /etc/rc.d/rc3.d/);do
if [[ $j =~ ^S.* ]];then FileC1=`echo $j | cut -c1`
echo -e "$j\tstart" case $FileC1 in
elif [[ $j =~ ^K.* ]];then K)
echo -e "$j\tstop" echo -e “$j\tstop”
else ;;
echo "it's not service file"
fi
done
v 4、编写脚本 ,提示输入正整数n 的值,计算1+2+…+n 的总和
read -p "give me a number:" n
sum=0
for((i=0;i<=$n;i++))
do
sum=$[$sum+$i]
done
echo $sum
v 5、编写脚本 ,提示请输入网络地址,如192.168.0.0 ,判断输入的网段中主机在线状态
read -p "Please give a network:" ip
pingip=`echo "$ip"| cut -d. -f1-3`.
if echo "$ip" | egrep'\<(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}
([0-9]|[1-9 ][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\>' &>/dev/null;then
#test only 15 host
for i in `seq 240 255`
do
if ping -c1 -W1 $pingip$i &> /dev/null;then
echo "$pingip$i is on work"
else
echo "$pingip$i is out of work"
fi
done
else
echo "$ip is useless"
fi
v 6、 打印 九九 乘法表
#方法1:for循环 if语句 #方法2:for循环 for语句
for i in {1..9} for i in {0..9}
do do
for j in {1..9} for j in $(seq $i)
do do
if [ $j -le $i ];then echo -ne “$i*$j=$[$i*$j]\t”
echo -ne "$i*$j=$[i*j]\t" #不换行打印i*j=*TAB done
else echo
break #结束这一层的循环;一般在if中使用 done
fi
done
echo #显示打印内容
done
v 7、在/testdir 目录下创建10个html文件, 文件名格式为数字N(从1 到10 )加随机8个字母,如1AbCdeFgH.html
if [ -a /testdir ];then
echo "/testdir is alreadly existing"
else
mkdir /testdir
fi
for i in $(seq 10);do
for j in `cat /dev/urandom|tr -dc "a-zA-Z"|head -c 8`
do
cd /testdir
touch $i$j.html
done
echo "$i$j.html has been created"
done
1.2.3 while 循环
while CONDITION; do
循环体
done
CONDITION :循环控制条件;进入循环之前,先做一次判断;每一次循环之后会再次做判断;条件为“true” ,则执行一次循环;直到条件测试状态为“false” 终止循环。
v 因此:CONDTION 一般应该有循环控制变量;而此变量的值会在循环体不断地被修正
v 进入条件:CONDITION 为true
v 退出条件:CONDITION 为false
1.2.4 while练习
1、编写脚本,求100 以内 所有正奇数之和.
sum=0;i=1
while ((i<$n))
do
sum=$[$sum+$i]
let i=$[$i+2]
done
echo "sum=$sum"
v 2、编写脚本, 提示请输入网络地址,如192.168.0.0 ,判断输入的网段中主机在线状态 ,并统计在线主机和离线主机各多少。
read -p "Please give a network:" ip
pingip=`echo "$ip"| cut -d. -f1-3`.
if echo "$ip" | egrep '\<(([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9
][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\>' &>/dev/null;then
#test only 15 host
for i in `seq 240 255`
do
if ping -c1 -W1 $pingip$i &> /dev/null;then
echo "$pingip$i is on work"
else
echo "$pingip$i is out of work"
fi
done
else
echo "$ip is useless"
fi unset ip pingip i
v 3、编写脚本,打印 九九乘法表
#方法3:while循环 #方法3:until循环
i=1 i=1
while [ $i -le 9 ];do #当$i的值小于9,就执行以下循环 until ((i>9))
j=1 do
while [ $j -le $i ];do #当$j的值小于$i,就执行以下循环 let j=1
echo -en "$i*$j=$[i*j]\t" until ((j>i))
let j++ #$j=$j+1 do
done echo -en “$i*$j=$[$i*$j]\t”
let i++ #$i=$i+1,重新开始循环 let j++
let j=1 #j的值从1开始,重新开始循环 echo
echo done
done
v 4、编写脚本,利用变量RANDOM 生成10 个随机数字,输出这个10 数字,并显示其中的最大值和最小值
max=$[$RANDOM] #变量赋值也可以用let max=$RANDOM
min=$[$RANDOM] #变量赋值也可以用$(( ));
#[ $max -lt $min ]&&let mid=$min&&let min=$max&&let max=$mid #举一个例子
#echo "random number 1 is : $max"
#echo "random number 2 is : $min"
for ((i=1;i<=10;i++));do
ran=$[$RANDOM] #与随机生成的中间参数做比较
echo "random number $i is : $ran"
[ $max -lt $ran ]&&let max=$ran
[ $min -gt $ran ]&&let min=$ran
done
echo "======================================="
echo "max number is $max , min number is $min"
v 5、编写脚本,实现打印国际象棋棋盘
read -p "How many lines will you want to make for chess:" line
for i in $(seq $line);do #控制行数
for j in $(seq $line);do #控制列数
let sum=$[i+j] #把不同的两行合并成一个单元
if [[ $sum%2 -eq 0 ]];then #取余,当为单数的话打印一种颜色,为双数打印另一种
echo -en "\e[47m \e[0m" #打印空格颜色为黑色
else
echo -en "\e[40m \e[0m" #打印空格颜色为白色
fi z=$[$sum%2]
done [ $z -eq 0 ] && echo -ne "\033[47;1m \033[0m"||echo -ne "\033[40;1m \033[0m"
echo
done
unset i j sum
v 6、后续六个字符串:154773ae5d 、bc3f3efe68、ada7aa2054 、4ee771de1f 、2ebd3caa45 、3417171ac1是通过对RANDOM 随机数变量执行命令:md5sum|cut -c1-10后 的结果,请破解这些字符串对应的的RANDOM值。
a=(
154773ae5d
bc3f3efe68
ada7aa2054
4ee771de1f
2ebd3caa45
3417171ac1
) #a是一个数组
for n in {0..65535};do
md5=`echo $n | md5sum |cut -c1-10`
for m in ${a[@]};do #${a[@]} 表示数组中的子集
if [[ "$md5" == "$m" ]];then
echo -e $md5 "–>" $n
fi
done
done
unset n m md5
1.2.5 until 循环
v until CONDITION; do
循环体
v done
v 进入条件: CONDITION 为false
v 退出条件: CONDITION 为true
1.3 循环控制语句
1.3.1 continue循环控制语句
v continue [N] :提前结束第N 层的本轮循环,而直接进入下一轮循环判断;最内层为第1层;用于循环体中
while CONDTIITON1; do
CMD1
…
if CONDITION2; then
continue
fi
CMDn
…
done
1.3.2 break循环控制语句
v break [N] :提前结束第N 层循环,最内层为第1层 ;用于循环体中,退出整个循环,继续执行循环后的命令
while CONDTIITON1; do
CMD1
…
if CONDITION2; then
break
fi
CMDn
…
done
1.3.3 shift循环控制命令
v shift [n]
v 用于将参量列表 list 左移指定次数,缺省为左移一次。
v 参量列表 list 一旦被移动,最左端的那个参数就从列表中删除。while 到循环遍历位置参量列表时,常用到 shift
v ./doit.sh a b c d e f g h
v ./shfit.sh a b c d e f g h
示例:doit.sh
#!/bin/bash
# Name: doit.sh
# Purpose: shift through command line arguments
# Usage: doit.sh [args]
while (( $# > 0 )) # or [ $# -gt 0 ];do
echo $*
shift
done
示例:shift.sh
#!/bin/bash
# Using 'shift' to step through all thepositional parameters.
until [ -z "$1" ];do
echo "$1"
shift
done
echo
exit 0
1.3.4创建无限循环
while true; do
循环体
done
until false; do
循环体
done
1.3.5 练习
1、每隔3秒钟到系统上获取已经登录的用户的信息;如果发现用户hacker 登录,则将登录时间和主机记录日志/var/log/login.log 中, 并退出脚本。 until false;do
while true ;do until who | grep “^hacker\>” &> /dev/null;do
if who | grep “^hacker\>” &> /dev/null;then sleep 3
break done
fi echo “$(date +: %F &T”)hacker logged on” >> /var/log/login.log
sleep 3
done
echo “$(date +: %F &T”)hacker logged on” >> /var/log/login.log
v 2、随机生成10 以内的数字,实现猜字游戏,提示比较大或小,相等则退出。
#!/bin/bash
suiji=$[$RANDOM%10+1] #随机生成10以内的数字
read -p "我猜:" shuru
until [[ $suiji -eq $shuru ]]; do
[ $shuru -lt $suiji ] && echo "小了"
[ $shuru -gt $suiji ] && echo "大了"
read -p "我猜:" shuru
done
echo "猜中了,退出"
v 3、用文件名做为参数,统计所有参数文件的总行数特殊用法。
1.3.6 循环的特殊用法
while特殊用法
遍历文件的每一行:while read line; do
循环体
done < /PATH/FROM/SOMEFILE
依次读取/PATH/FROM/SOMEFILE 文件中的每一行,且将行赋值给变量line。
练习
扫描/etc/passwd 文件每一行,如发现GECOS 字段为空,则填充用户名和单位电话为62985600 ,并提示该用户的GECOS信息修改成功。
while read line;do
if [ -z “`awk -F: ‘{print $5}’ $line`” ] ; then if [-z “`echo $line|cut -d: -f5`”] ;then
chfn -p 62985600 -f 62985600 `awk -F: ‘{print $1}’ $line` &> /dev/null
echo “The user GECOS have been changed successful”
fi
done < etc/passwd
unset line
for特殊用法
v 双小括号方法,即((…)) 格式,也可以用于算术运算
v 双小括号方法也可以使bash Shell 实现C 语言风格的变量操作
#I=10 ; #((I++))
for 循环的特殊格式:
for (( 控制变量初始化; 条件判断表达式; 控制变量的修正表达式))
do
循环体
done
v 控制变量初始化:仅在运行到循环代码段时执行一次
v 控制变量的修正表达式:每轮循环结束会先进行控制变量修正运算,而后再做条件判断
1.3.7 select 循环与菜单
select variable(变量) in list
do
循环体命令
done
v select循环主要用于创建菜单,按数字顺序排列的示 菜单项将显示在标准错误上,并显示 PS3提示符,等待用户输入。
用户输入菜单列表中的某个数字,执行相应的命令
v 用户输入被保存在内置变量 REPLY 中。
select 与case
vselect 是个无限循环,因此要记住用 break 命令退出循环,或用exit按命令终止脚本。也可以按 ctrl+c退出循环。
vselect 和经常和 case 联合使用
v与 for循环类似,可以省略 in list ,此时使用位置参量
1.3.8 trap信号捕捉
trap ' 触发指令' 信号
自定义进程收到系统发出的指定信号后,将执行触发指令,而不会执行原操作
trap ' ' 信号:忽略信号的操作
trap '-' 信号:恢复还信号的操作
trap -p:列出自定义信号操作
1.3.9 trap 示例
#!/bin/bash
trap 'echo “signal:SIGINT"' int
trap -p
for((i=0;i<=10;i++))
do
sleep 1
echo $i
done
trap '' int
trap -p
for((i=11;i<=20;i++))
do
sleep 1
echo $i
done
trap '-' int
trap -p
for((i=21;i<=30;i++))
do
sleep 1
echo $i
done
二、函数介绍
v 函数function 是由若干条shell 命令组成的语句块,实现代码重用和模块化编程。
v它与shell 程序形式上是相似的,不同的是它不是一个单独的进程,不能独立运行,而是shell 程序的一部分。只能被调用。
v 函数和shell 程序比较相似,区别在于:
Ø Shell 程序在子Shell 中运行。
Ø 而Shell 函数在当前Shell 中运行。因此在当前Shell 中,函数可以对shell 中变量进行修改。
匿名函数:{cmd1;cmd2;cmd3},不定义在set的函数变量中。
2.1定义函数
v 函数由两部分组成:函数名f_name和函数体。
v 语法一:
function f_name {
… 函数体…
}
v 语法二:
function f_name () {
… 函数体…
}
v最常用语法三:
f_name (){
… 函数体…
}
2.2函数使用
v 函数的定义和使用:
1.可在交互式环境下定义函数 ; 2.可将函数放在脚本文件中作为它的一部分 ; 3.可放在只包含函数的单独文件中。
v 调用:函数只有被调用才会执行
函数的调用:给定函数名;函数名出现的地方,会被自动替换为函数代码。
函数的生命周期:被调用时创建,返回时终止,只能在当前shell中进行。
2.2.1函数返回值
函数有两种返回值:
v 函数的执行结果返回值:
(1) 使用echo等命令进行输出 ; (2) 函数体中调用命令的输出结果。
v 函数的退出状态码:
(1) 默认取决于函数中执行的最后一条命令的退出状态码 ;
(2) 自定义退出状态码, 其格式为:在函数中 return #从函数中返回,用最后状态命令决定返回值。
return 0 无错误返回。退出函数,但是继续执行函数后面的命令;return 1-255 有错误返回。不执行后面任务。
(echo $? 的值为return 返回的值)
function函数存放目录:/etc/init.d/functions;在自己脚本中调用的话,用source /root/bin/f_name 就可执行函数
交互式环境下定义和使用函数(不常用)
v 示例:
$dir() {
> ls -l
> }
v 定义该函数后,若在$ 后面键入dir ,其显示结果同ls -l的作用相同。
$dir 函数:
v 该dir 函数将一直保留到用户从系统退出(仅对当前终端有效),或执行了如下所示的unset 命令:$ unset dir
unset f_name :删除函数 ;set f_name :查看定义的函数
在脚本中定义及使用函数
v 函数在使用前必须定义,因此应将函数定义放在脚本开始部分,直至shell 首次发现它后才能使用
v 调用函数仅使用其函数名即可。
v 示例:
$cat func1
#!/bin/bash
# func1
hello()
{
echo "Hello there today's date is `date +%F`"
}
echo "now going to the function hello"
hello
echo "back from the function"
2.2.2使用函数文件
v 可以将经常使用的函数存入函数文件,然后将函数文件载入shell 。
v 文件名可任意选取,但最好与相关任务有某种联系。例如:functions.main
v 一旦函数文件载入shell ,就可以在命令行或脚本中调用函数。可以使用set 命令查看所有定义的函数,其输出列表包括已经载入shell 的所有函数。
v 若要改动函数,首先用unset 命令从shell 中删除函数。改动完毕后,再重新载入此文件。
创建函数文件
v 函数文件示例:
$cat functions.main
#!/bin/bash
#functions.main
findit()
{
if [ $# -lt 1 ] ; then
echo "Usage:findit file"
return 1
fi
find / -name $1 –print
}
载入函数
v 函数文件已创建好后,要将它载入shell
v 定位函数文件并载入shell 的格式:. filename 或 source filename
随后,函数就被载入到set 所定义的函数里了,就可以在脚本里直接使用函数名了
v 注意:此即< 点> < 空格> < 文件名>
这里的文件名要带正确路径
v 示例:上例中的函数,可使用如下命令: . functions.main
检查载入函数
v 使用set 命令检查函数是否已载入。set 命令将在shell 中显示所有的载入函数。
v 示例:
$set
findit=( )
{
if [ $# -lt 1 ]; then
echo "usage :findit file";
return 1
fi
find / -name $1 -print
}
…
2.2.3执行shell 函数
v 要执行函数,简单地键入函数名即可:
$findit groups
/usr/bin/groups
/usr/local/backups/groups.bak
删除shell 函数
v 现在对函数做一些改动。首先删除函数,使其对shell 不可用。使用unset 命令完成此功能.
v 命令格式为:unset function_name
v 示例 :$unset findit ; 再键入set 命令,函数将不再显示
2.3函数参数
v 函数可以接受参数:
传递参数给函数:调用函数时,在函数名后面以空白分隔给定参数列表即可;例如“testfunc arg1 arg2 …”
在函数体中当中,可使用$1, $2, … 调用这些参数;还可以使用$@, $*, $# 等特殊变量
2.3.1函数变量
变量作用域:
环境变量:当前shell 和子shell 有效
本地变量:只在当前shell 进程有效,为执行脚本会启动专用子shell 进程;因此,本地变量的作用范围是当前shell 脚本程序文件,包括脚本中的函数。
局部变量:函数的生命周期;函数结束时变量被自动销毁
v 注意:如果函数中有局部变量,如果其名称同本地变量,使用局部变量。
v 在函数中定义局部变量的方法:local NAME=VALUE
2.3.2函数递归
函数直接或间接调用自身;注意递归层数
v 递归实例:阶乘是基斯顿·于卡曼于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 $1
注意:引入变量先要用read
2.3.3练习
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 命令管理
lockfile=(/var/log/subsys/SCRIPT_NAME)
usagte(){
echo "Usage: $prog {start|stop|restart|status}"
}
if [ $# -lt 1 ]; then
usage
exit 1
fi
start(){
if [ -e $lockfile ]; then
echo "$prog is already running."
return 0
else
touch $lockfile
[ $? -eq 0 ] && echo "Starting $prog finished."
fi
}
stop(){
if [ -e $lockfile ]; then
rm -f $lockfile && echo "Stop $prog ok."
else
echo "$prog is stopped yet."
fi
}
status(){
if [ -e $lockfile ]; then
echo "$prog is running."
else
echo "$prog is stopped."
fi
}
case $1 in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
status)
status
;;
*)
usage
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/ld-linux-x86-64.so.2
(5) 每次复制完成一个命令后,不要退出,而是提示用户键入新的要复制的命令,并重复完成上述功能;直到用户输入quit.
#!/bin/bash
ch_root="/mnt/sysroot"
[ ! -d $ch_root ] && mkdir $ch_root
bincopy() {
if which $1 $>/dev/null; then
local cmd_path="which –skip-alias $1"
local bin_dir="dirname $cmd_path"
[ -d ${ch_root}${bin_dir} ] || mkdir -p ${ch_root}${bin_dir}
[ -f ${ch_root}${cmd_path}] || cp $cmd_path ${ch_root}${bin_dir}
return 0
else
echo "Command not found."
return 1
fi
}
libcopy() {
local lib_list=$(ldd `which –skip-alias $1` | grep -Eo '/[^[:space:]]+')
for loop in $lib_list;do
local lib_dir=`dirname $loop`
[ -d $ {ch_root}${lib_dir} ] || mkdir -p ${ch_root}${lib_dir}
[ -f $ {ch_root}${loop} ] || cp $loop ${ch_root}${lib_dir}
done
}
read -p "Please input a command:" command
while [ "$command" != "quit" ]; do
if bincopy $command ; then
libcopy $command
fi
read -p "Please input a command or quit: "command
done
3 、编写函数实现两个数字做为参数,返回最大值.
#!/bin/bash
source funs.sh
funs.sh
#!/bin/bash
echo "please enter two number"
read a
read b
if test $a -eq $b;then
echo "two same: "$a
elif test $a -gt $b;then
echo "big is: "$a
else
echo "big is: "$b
fi
4 、编写函数实现数字的加减乘除运算,例如输入 1 + 2 ,将得出正确结果。
#!/bin/bash
source funs.sh
jia $1 $2
jian $1 $2
cheng $1 $2
chu $1 $2
funs.sh
#!bin/bash
echo "1st arg is $1"
echo "2nd arg is $2"
jia (){
local a=$[$1+$2]
echo $a
}
jian (){
local a=$[$2-$1]
echo $a
}
cheng (){
local a=$[$1*$2]
echo $a
}
chu (){
local a=$[$1/$2]
echo $a
}
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 阶斐波那契数列。
fact() {
if [ $1 -eq 0 ]
then
echo 0
elif [ $1 -eq 1 ]
then
echo 1
else
echo $[$(fact $[$1-2])+$(fact $[$1-1])]
fi
}
6 、汉诺塔(又称河内塔)问题是源于印度一个古老传说。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着64 片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。
利用函数,实现N 片盘的汉诺塔的移动步骤。
#!/bin/bash
step=0
hanoi(){
[[ ! $1 = ~ ^[1-9][0-9]*$ ]]&&echo "error! please input a positive interger" && exit
if [ $1 -eq 1 ]; then
let step++
echo "$step: move plate $1 $2 —–> $4"
else
hanoi "$[$1-1]" $2 $4 $3
let step++
echo "$step: move plate $1 $2 —–> $4"
hanoi "$[$1-1]" $3 $2 $4
fi
}
read -p "please input the number of plates: "number
hanoi $number A B C
三、数组
v 变量:存储单个元素的内存空间
v 数组:存储多个元素的连续的内存空间,相当于多个变量的集合。
数组名和索引
索引:编号从0 开始,属于数值索引
注意:索引可支持使用自定义的格式,而不仅是数值格式,即为关联索引 ,bash4.0 版本之后开始支持。
bash 的数组支持稀疏格式(索引之间不连续)
声明数组:
declare -a ARRAY_NAME 可以不声明,但是建议声明
declare -A ARRAY_NAME: 关联数组 ;必须要先声明才能使用
3.1数组赋值
v 数组元素的赋值:
(1) 一次只赋值一个元素;
ARRAY_NAME[INDEX]下标=VALUE
weekdays[0]="Sunday" ; weekdays[4]="Thursday" 稀疏格式
(2) 一次赋值全部元素:ARRAY_NAME=("VAL1" "VAL2" "VAL3" …)
生成列表可以用ARRAY_NAME=;ARRAY_NAME=({1..10});ARRAY_NAME=(*)
(3) 只赋值特定元素:指定下标,直接赋值
ARRAY_NAME=([0]="VAL1" [3]="VAL2" …)
(4) 交互式数组值对赋值:read -a ARRAY
3.2引用数组
v 引用数组元素:${ARRAY_NAME[INDEX]} 注意:省略[INDEX] 表示引用下标为0 的元素
数组的长度( 数组中元素的个数): 当不知道数组中存在多少元素
${ARRAY_NAME[*]}、${ARRAY_NAME[@]}:查看显示数组中所有元素
${#ARRAY_NAME[*]}、${#ARRAY_NAME[@]}:查看显示数组的元素个数 ,新加的元素的下标就等于元素的个数
如果数组要新添加一个元素 name[${ARRAY_NAME[@]}]=”var”
v 示例:生成10 个随机数保存于数组中,并找出其最大值和最小值
#!/bin/bash
declare -a rand
declare -i max=0
declare -i min=32767
for i in {0..9}; do
rand[$i]=$RANDOM
echo ${rand[$i]}
[ ${rand[$i]} -gt $max ] && max=${rand[$i]}
[ ${rand[$i]} -lt $min ] && min=${rand[$i]}
done
echo "Max: $max Min:$min"
v 编写脚本 ,定义一个数组,数组中的元素是/var/log 目录下所有以.log 结尾的文件;要统计其下标为偶数的文件中的行数之和。
#!/bin/bash
#
declare -a files
files=(/var/log/*.log)
declare -i lines=0
for i in $(seq 0 $[${#files[*]}-1]); do fileline=`wc -l ${file[$i]} |cut -d " " -f1`
if [ $[$i%2] -eq 0 ];then ——》for i in $(seq 0 2 $[${#file[*]}-1];do
let lines+=$(wc -l ${files[$i]} | cut -d' ' -f1) [ $[$i%2] -eq 0 ] && let lines+=$fileline
fi
done
echo "Lines: $lines."
3.3数组数据处理
引用数组中的元素:
所有元素:${ARRAY[@]} ; ${ARRAY[*]}
数组切片:${ARRAY[@]:offset:number} 跳offset个元素;取随后的number个元素
offset: 要跳过的元素个数 ; number: 要取出的元素个数
取偏移量之后的所有元素:${ARRAY[@]:offset}
向数组中追加元素:ARRAY[${#ARRAY[*]}]=“var”
删除数组中的某元素:导致稀疏格式:unset ARRAY[INDEX]
关联数组:declare -A ARRAY_NAME 注意:必须先声明,再调用
ARRAY_NAME=([idx_name1]='val1' [idx_name2]='val2’…)
现有的数组只能先unset ,再重新开始设置
练习1 、输入若干个数值存入数组中,采用 冒泡算法进行升序或降序排序
#!/bin/sh
echo "please input a number list:"
read -a arr
for (( i=0 ; i<${#arr[@]} ; i++ ))
do
for (( j=${#arr[@]}- 1 ; j>i ; j– ))
do
#echo $j
if [[ ${arr[j]} -lt ${arr[j-1]} ]]
then
t=${arr[j]}
arr[j]=${arr[j-1]}
arr[j-1]=$t
fi
done
done
echo "after ascending sorting:"
echo ${arr[@]}
=================================================
for (( i=0 ; i<${#arr[@]} ; i++ ))
do
for (( j=${#arr[@]}- 1 ; j>i ; j– ))
do
#echo $j
if [[ ${arr[j]} -gt ${arr[j-1]} ]]
then
t=${arr[j]}
arr[j]=${arr[j-1]}
arr[j-1]=$t
fi
done
done
echo "after descending sorting:"
echo ${arr[@]}
3.4字符串处理
bash的字符串处理工具
3.4.1字符串切片
${#var}: 返回字符串变量var 的长度
${var:offset}: 返回字符串变量var 中从第offset 个字符后(不包括第offset 个字符)的字符开始,到最后的部分,offset的取值在0 到 ${#var}-1 之间(bash4.2 后,允许为负值)
${var:offset:number} :返回 字符串变量var 中从第offset 个字符后(不包括第offset 个字符)的字符开始长度为number 的部分
${var: -lengh} :取字符串的最右侧几个字符
注意:冒号后必须有一空白字符
${var:offset: -lengh} :从最左侧跳过offset 字符,一直取到字符串的最右侧lengh个字符之前
3.4.2基于模式取子串
${var#*word} :其中word 可以是指定的任意字符
功能:自左而右,查找var 变量所存储的字符串中,第一次出现的word, 删除字符串开头至第一次出现word 字符之间的所有字符。
${var##*word} :同上,不同的是,删除的是字符串开头至最后一次由word 指定的字符之间的所有内容
示例:file="var/log/messages”
echo ${file#*/}: log/messages ; echo ${file##*/}: messages
${var%word*} :其中word 可以是指定的任意字符;
功能:自右而左,查找var 变量所存储的字符串中,第一次出现的word, 删除字符串最后一个字符向左至第一次出现word 字符之间的所有字符;
${var%%word*} :同上,只不过删除字符串最右侧的字符向左至最后一次出现word 字符之间的所有字符;
file="/var/log/messages"
echo ${file%/*}: /var/log ; echo ${file%%/*}: 空
示例:url=http://www.magedu.com:80
echo ${url##*:} 80 ; echo ${url%%:*} http
3.4.3查找替换
${var/pattern/substi} :查找var 所表示的字符串中,第一次被pattern 所匹配到的字符串,以substi 替换之
${var//pattern/substi}: 查找var 所表示的字符串中,所有能被pattern 所匹配到的字符串,以substi 替换之
${var/#pattern/substi} :查找var 所表示的字符串中,行首被pattern 所匹配到的字符串,以substi 替换之
${var/%pattern/substi} :查找var 所表示的字符串中,行尾被pattern 所匹配到的字符串,以substi 替换之
3.4.4查找并删除
${var/pattern} :查找var 所表示的字符串中,删除第一次被pattern 所匹配到的字符串
${var//pattern} :所有
${var/#pattern} :行首
${var/%pattern} :行尾
3.4.5字符大小写转换
${var^^} :把var 中的所有小写字母转换为大写
${var,,} :把var 中的所有大写字母转换为小写
3.5变量赋值
v ${var:-value} :如果var 为空或未设置,那么返回value ;否则,则返回var的值
v ${var:+value} :如果var 不空,则返回value ,否则返回空值
v ${var:=value} :如果var 为空或未设置,那么返回value ,并将value 赋值给var ;否则,则返回var的值
v ${var:?error_info} :如果var 为空或未设置 ,那么在当前终端打印error_info;否则,则返回var的值
为脚本程序使用配置文件, 实现变量赋值
(1) 定义文本文件,每行定义“name=value”
(2) 在脚本中source 此文件即可
高级变量用法– 有类型变量
v Shell 变量一般是无类型的,但是bash Shell 提供了declare和typeset 两个命令用于指定变量的类型,两个命令是等价的。
3.5.1 declare变量
v declare [ 选项] 变量名
-r:将变量设置为只读属性 ; -x:将变量声明为环境变量 ;-f:显示此脚本前定义过的所有函数名及其内容
-i:将变量定义为整型数 ; -a:将变量定义为数组 ;-F:仅显示此脚本前定义过的所有函数名
-A:将变量定义为关联数组
-l:将变量值转为小写字母 declare -l var=UPPER ; -u:将变量值转为大写字母 declare -u var=lower
3.5.2间接变量引用
v 如果第一个变量的值是第二个变量的名字,从第一个变量引用第二个变量的值就称为间接变量引用。
v variable1=variable2 ; variable2=value
v variable1 的值是variable2 ,而variable2 又是变量名,variable2 的值为value ,间接变量引用是指通过variable1获得变量值value 的行为
v bash Shell 提供了两种格式实现间接变量引用
eval tempvar=\$$variable1 ; tempvar=${!variable1}
示例: [root@server ~]# N=NAME
[root@server ~]# NAME=wangxiaochun
[root@server ~]# N1=${!N}
[root@server ~]# echo $N1
wangxiaochun
[root@server ~]# eval N2=\$$N
[root@server ~]# echo $N2
wangxiaochun
3.5.3 eval 命令
v eval 命令将会首先扫描命令行进行所有的置换,然后再执行该命令。该命令适用于那些一次扫描无法实现其功能的变量. 该命令对变量进行两次扫描(如果变量是命令,则显示执行的结果)。
示例: [root@server ~]# CMD=whoami
[root@server ~]# echo $CMD
whoami
[root@server ~]# eval $CMD
root
3.5.4 mktemp创建临时文件
mktemp 命令:创建的临时文件可避免冲突
v mktemp [OPTION]… [TEMPLATE]
TEMPLATE: filename.XXX 注意:X 至少要出现三个,创建并显示创建的临时文件的名字
-d: 创建临时目录 ; -p DIR 或–tmpdir=DIR :指明临时文件所存放目录位置
示例 :
#mktemp /tmp/test.XXX 计划任务crontab -e 输出重定向到/dev/null;或者用此临时文件爱你
#tmpdir=`mktemp –d /tmp/testdir.XXX`
#mktemp –tmpdir=/testdir test.XXXXXX
3.5.5 install安装复制文件
v install 命令:适合安装程序,能直接给予文件的所有者,所有组以及权限
install [OPTION]… [-T] SOURCE DEST 单文件 ; install [OPTION]… SOURCE… DIRECTORY
install [OPTION]… -t DIRECTORY SOURCE… ; install [OPTION]… -d DIRECTORY… 创建空目录
-m MODE:指明权限 ,默认755 ; -o OWNER:指明属主 ; -g GROUP:指明属组
示例:
install -m 700 -o wang -g admins file1 file2 相当于cp,不过给予了权限
install –m –d /testdir/installdir 直接创建目录
原创文章,作者:lyx,如若转载,请注明出处:http://www.178linux.com/61073