一、Shell 编程之条件结构
1、Shell 条件测试语法
test 测试表达式 | 利用test命令进行条件测试表达式, test命令与测试表达式之间至少有一个空格 |
[ 测试表达式 ] | 通过[ ]中括号进行条件测试表达式, []中括号边界与测试表达式之间至少有一个空格 |
[[ 测试表达式 ]] | 通过[[ ]]双中括号进行条件测试表达式, [[ ]]双中括号与测试表达式之间至少有一个空格 |
((测试表达式)) | 通过(( ))双小括号进行条件测试表达式, ( ))双小括号两端不需要空格, 常用于整数对比 |
1、符号说明
‐su: syntax error near unexpected token `} '
[[email protected] ~]# { var=notest;echo $var;}
notest
[[email protected] ~]# echo $var
notest
# {}修改了变量的值。表明在当前shell中运行的
[[email protected] ~]# var=test
[[email protected] ~]# echo $var
test
[[email protected] ~]# (var=notest;echo $var)
notest
[[email protected] ~]# echo $var
test
# () 里的执行完毕后没有改变变量的值, 说明在子shell中执行的
# $(( ))和$[ ]的用途一致 ,用来作整数运算。在 bash 中, $(( ))的整数运算符号大致有这些: # + ‐ * / 加、减、乘、除
# % 余数运算
# & | ^ ! AND、OR、XOR、NOT运算
举例:
[[email protected] ~]# a=5; b=7; c=2
[[email protected] ~]# echo $((a+b*c))
[[email protected] ~]# echo $[a+b*c]
[[email protected] ~]# echo $(((a+b)/c))
[[email protected] ~]# echo $[(a+b)/c]
6
[[email protected] ~]# echo $(((a*b)%c))
[[email protected] ~]# echo $[(a*b)%c]
1
# $(( ))中的变量名称也可以在其前面加 $ 符号: $(($a+$b*$c))也可以得到 19 的结果。
# $(( ))还可以作不同进制(如二进制、八进位、十六进制)运算, 只是输出结果皆为十进制而已。
[[email protected] ~]# echo $((16#2a)) # 16进位转十进制
42
# 当前的 umask 是 022, 新建文件的权限为:
[[email protected] ~]# umask 022
[[email protected] ~]# echo "obase=8; $(( 8#666 & (8#777 ^ 8#$(umask)) ))" | bc
[[email protected] ~]# 644
# 单纯用(( ))也可以重定义变量值, 或作testing:
[[email protected] ~]# a=5
[[email protected] ~]# echo $((a++))
# 将 a 重定义为 6
双中括号[[ ]]中可以使用通配符进行匹配, 这是其区别于其它几种语法的地方
&&, | |, <, >等操作符可用于双中括号[[ ]]中, 但不能应用于[ ]中, 在[ ]中一般用-a, -o, -lt, -gt来代替
举例:
|
2、获取帮助
|
2、Shell 测试表达式用法
1、文件测试表达式
-d 文件 | 文件存在且为目录则为真 |
-f 文件 | 文件存在且为普通文件则为真 |
-e 文件 | 文件存在则为真, 不辩别是目录还是文件 |
-s 文件 | 文件存在且文件大小不为0则为真 |
-r 文件 | 文件存在且可读则为真, 与执行脚本的用户权限也有关 |
-w 文件 | 文件存在且可写则为真, 与执行脚本的用户权限也有关 |
-x 文件 | 文件存在且可执行则为真, 与执行脚本的用户权限也有关 |
-L 文件 | 文件存在且为链接文件则为真 |
f1 -nt f2 | 文件f1比文件f2新则为真, 根据文件的修改时间计算 |
f1 -ot f2 | 文件f1比文件f2旧则为真, 根据文件的修改时间计算 |
文件测试 [ 操作符 文件或目录 ]
[[email protected] ~]# test ‐d /home
[[email protected] ~]# echo $?
0
[[email protected] ~]# test ‐d /home11111
[[email protected] ~]# echo $?
1
[[email protected] ~]# [ ‐d /home ]
[[email protected] ~]# [ ! ‐d /ccc ] && mkdir /ccc
[[email protected] ~]# [ ‐d /ccc ] | | mkdir /ccc
2、字符串测试表达式
# 提示: 字符串必须使用双引号
[[email protected] ~]# [ "$USER" = "root" ];echo $?
0
[[email protected] ~]# [ "$USER" == "root" ];echo $?
0
[[email protected] ~]# BBB=""
[[email protected] ~]# echo ${#BBB}
0
[[email protected] ~]# [ ‐z "$BBB" ]
[[email protected] ~]# echo $?
0
[[email protected] ~]# [ ‐n "$BBB" ]
[[email protected] ~]# echo $?
1
[[email protected] ~]# var2=
[[email protected] ~]#
[[email protected] ~]# echo ${#var1}
3
[[email protected] ~]# echo ${#var2}
0
[[email protected] ~]# echo ${#var3}
0
[[email protected] ~]# [ ‐z "$var1" ];echo $?
1
[[email protected] ~]# [ ‐z "$var2" ];echo $?
0
[[email protected] ~]# [ ‐z "$var3" ];echo $?
0
[[email protected] ~]# [ ‐n "$var1" ];echo $?
0
[[email protected] ~]# [ ‐n "$var2" ];echo $?
参数 | 功能 | |
-z | 1 | 如果字符串s1的长度为0, 则测试条件为真 |
-n | 1 | 如果字符串s1的长度大于0, 则测试条件为真 |
sl | 如果字符串s1不是空字符串, 则测试条件为真 | |
=或== | s1=s2 | 如果s1等于s2, 则测试条件为真,“=”前后应有空格 |
!= | s1!=s2 | 如果s1不等于s2, 则测试条件为真 |
< | 1 | 如果按字典顺序s1在s2之前, 则测试条件为真 |
> | s1>s2 | 如果按自定顺序s1在s2之后, 则测试条件为真 |
1、注意
对于字符串的比较, 一定要将字符串加比引号后再比较。如[ -n "$string" ]
=与!=可用于判断两个字符串是否相同
2、字符串比较
# 提示: 字符串必须使用双引号
[[email protected] ~]# [ "$USER" = "root" ];echo $?
0
[[email protected] ~]# [ "$USER" == "root" ];echo $?
0
[[email protected] ~]# BBB=""
[[email protected] ~]# echo ${#BBB}
0
[[email protected] ~]# [ ‐z "$BBB" ]
[[email protected] ~]# echo $?
0
[[email protected] ~]# [ ‐n "$BBB" ]
[[email protected] ~]# echo $?
1
[[email protected] ~]# var2=
[[email protected] ~]#
[[email protected] ~]# echo ${#var1}
3
[[email protected] ~]# echo ${#var2}
0
[[email protected] ~]# echo ${#var3}
0
[[email protected] ~]# [ ‐z "$var1" ];echo $?
1
[[email protected] ~]# [ ‐z "$var2" ];echo $?
0
[[email protected] ~]# [ ‐z "$var3" ];echo $?
0
[[email protected] ~]# [ ‐n "$var1" ];echo $?
0
[[email protected] ~]# [ ‐n "$var2" ];echo $?
3、整数操作符
在[]和test中使用 | 在[[ ]]和(( ))中使用 | 说明 |
-eq | ==或= | 等于, 全拼为equal |
-nq | != | 不等于, 全拼为not equal |
-gt | > | 大于, 全拼为greater than |
-ge | >= | 大于等于, 全拼为greater equal |
-lt | < | 小于, 全拼为less than |
-le | <= | 小于等于, 全拼为less equal |
1、判断变量是不是数字
|
|
3、C语言风格的数值比较
|
4、实例
# 案例1:
[[email protected] ~]# cat test02.sh #!/bin/bash
# 判断用户输入的是否是数字
read ‐p "请输入一个数值: " num
if [[ ! "$num" =~ ^[0 ‐9]+$ ]];then echo "你输入的不是数字, 程序退出!!!"
exit
fi
echo ccc
# 案例2:
[[email protected] ~]# cat test03.sh # !/bin/bash
while :
do
if [[ $num =~ ^[0 ‐9]+$ ]];then
break
else
read ‐p "不是数字, 请重新输入数值: " num fi
done
echo "你输入的数字是: $num"
4、逻辑操作符
在[]和test中使用 | 在[[ ]]和(( ))中使用 | 说明 |
-a | && | and, 与, 两端都为真, 则结果为真 |
-o | | | | or, 或, 两端有一个为真, 则结果为真 |
! | ! | not, 非, 两端相反, 则结果为真 |
|
5、测试表达式的区别总结
测试表达 式符号 | test | [ ] | [[ ]] | (( )) |
边界是否 需要空格 | 需要 | 需要 | 需要 | 不需要 |
逻辑操作 符 | !、-a、 -o | !、-a、 -o | !、&&、 | | | !、&&、 | | |
整数比较 操作符 | -eq、-ne、-lt、- gt、-ge、-le | -eq、-ne、-lt、- gt、-ge、-le | -eq、-ne、-lt、-gt、-ge、-le或 =、!=、<、>、>=、<= | =、!=、<、 >、>=、<= |
字符串比 较操作符 | =、==、!= | =、==、!= | =、==、!= | =、==、!= |
是否支持 通配符 | 不支持 | 不支持 | 支持 | 不支持 |
1、变量为空或未定义长度都为0
|
2、Shell 脚本执行测试
# 执行脚本: [[email protected] ~]# ./01.sh [[email protected] ~]# bash 01.sh | # 需要执行权限 # 不需要执行权限 | 在子shell中执行 在子shell中执行 | |
[[email protected] ~]# . 01.sh # 不需要执行权限 在当前shell中执行 [[email protected] ~]# source 01.sh # 不需要执行权限 在当前shell中执行 # 提示: 通常修改系统配置文件中如 /etc/profile 的PATH等变量后, 使之在当前shell中生效 | |||
# 调试脚本: [[email protected] ~]# sh ‐n 02.sh [[email protected] ~]# sh ‐vx 02.sh | # 仅调试 syntax error # 以调试的方式执行, 查询整个执行过程 |
3、Shell 分支if语句
1、单分支 IF 条件语句
1、语法格式
|
2、实例
2、双分支语句
1、语法格式
|
2、实例
|
3、多分支语句
1、语法格式
|
2、实例
|
4、Shell 分支case语句
case 语句和 if...elif...else 语句一样都是多分支条件语句, 不过和多分支 if 条件语句不同的是, case 语句只能判断 一种条件关系, 而 if 语句可以判断多种条件关系。
1、case 语法格式
|
2、case 语句的使用总结
case 语句比较适合变量值较少且为固定的数字或字符串集合情况(非不确定的内容, 例如范围), 如果变量的值 是已知固定的start/stop/restart等元素, 那么采用case语实现就比较适合
case主要是写服务的启动脚本, 一般情况下, 传参不同且具有少量的字符串, 其适用范围窄
if就是取值判断、比较、应用比case更广。几乎所有的case语句都可以用if条件语句实现
case语句就相当于多分支的if/elif/else语句, 但case语句的优势是更规范、易
3、case 语句案例
1、判断输入内容
1.apple
2.pear
3.banana
4.cherry
# 当用户输入对应的数字选择水果的时候, 告诉他选择的水果是什么, 并给水果单词加上一种颜色 (随意) , 要求用 case语句实现。
[[email protected] ~]# cat fruit.sh
#!/bin/bash
##############################################################
# File Name: fruit.sh
# Version: V1.0
# Author: qfedu
# Organization:
# Created Time :
# Description:
##############################################################
cat <<EOF
1.apple
2.pear
3.banana
4.cherry
EOF
read ‐p "请输入您的选择:" num
red="\033[31m"
green="\033[32m"
yewllo="\033[33m"
blue="\033[34m"
tailer="\033[0m"
case $num in
1)
echo ‐e "$red apple $tailer"
;;
2)
echo ‐e "$green pear $tailer"
;;
3)
echo ‐e "$yewllo banana $tailer"
;;
4)
echo ‐e "$blue cherry $tailer"
;;
*)
echo "Usage:$0{1 |2 |3 |4}"
exit 1
esac
2、判断输入执行输入指令
[[email protected] ~]# cat rsync.sh
#!/bin/bash
##############################################################
# File Name: rsync.sh
# Version: V1.0
# Author: qfedu
# Organization:
# Created Time :
# Description:
##############################################################
. /etc/init.d/functions
# rsyncd进程号路径
rsyncd_pid_path=/var/run/rsyncd.pid
# 创建锁文件
lockfile=/var/lock/subsys/rsyncd
start() {
if [ ! ‐f $rsyncd_pid_path ]
then
rsync ‐‐daemon
retval=$?
if [ $retval ‐eq 0 ]
then
action "rsync is start ok" /bin/true
touch $lockfile
return $retval
else
action "rsync is start fail" /bin/false
return $retval
fi
else
echo "rsync in runing.."
fi
}
stop() {
if [ ‐f $rsyncd_pid_path ]
then
rsyncd_pid=`cat $rsyncd_pid_path`
#判断进程是否存在
if (kill ‐0 $rsyncd_pid &>/dev/null)
then
kill $rsyncd_pid
retval=$?
if [ $retval ‐eq 0 ]
then
action "rsync is stop ok" /bin/true
rm ‐f $lockfile
return $retval
else
action "rsync stop fail" /bin/false
return $retval
fi
fi
else
echo "$rsyncd_pid_path is not exist or rsyncd does not startup"
fi
}
case $1 in
start)
start
retval=$?
;;
stop)
stop
retval=$?
;;
restart)
stop
retval=$?
sleep 1
start
retval=$?
;;
*)
echo "Usage:$0{start|stop|restart}"
exit 1
esac
exit $retval
二、Shell 编程之循环结构
1、Shell 循环 for 语句
for循环的运作方式, 是讲串行的元素意义取出, 依序放入指定的变量中, 然后重复执行含括的命令区域 (在do和
done 之间) , 直到所有元素取尽为止。
其中, 串行是一些字符串的组合, 彼此用$IFS所定义的分隔符 (如空格符) 隔开, 这些字符串称为字段。
1、for 循环的语法结构
|
2、for 语法说明
for 每次从值集合中取一个值赋值给变量
do - done 将赋值后的变量带入执行的命令得到执行结果,
重复以上两个步骤, 直到值集合中的值被一一获取赋值给变量的到所有结果, 循环结束
3、实例
1、用 for 循环创建 demo1-demo10, 然后在 demo1-demo10 创建 test1-test10 的目录
|
| # 对/var目录中每一个文件, 进行for循环处理 # 如果/var下的文件是目录, 则使用du ‐sh计算该目录占用磁盘空间的大小 |
2、Shell 循环 while 语句
1、while 循环语法结构
|
2、while 语法说明
while 首先进行条件测试, 如果传回值为0 (条件测试为真) , 则进入循环, 执行命令区域, 否则不进入循环 满足 while 测试条件, 执行命令区域, 直到 while 的测试条件不满足结束执行while循环 (如果条件一直满足 执行无穷循环) 。
3、实例1while 循环读取文件的内容
| # 使用read有标准输入读取数据, 赋值变量 demo 中, 如果读到的数据非空, 就 |
|
4、实例2while 条件测试
| # 声明设置 i 和 sum为整数型 # while 条件测试: 只要i值小于或者等于10, 就执行循环 # sum+=i 和 sum=sum+i 是一样的, sum累加上i # let i++, i 的值递增 1, 此行是改变条件测试的命令, 一旦 i 大于10, 可终止循环 # 遇到 done, 回到 while 条件测试 # 直到 while 条件不满足, 显示 sum 的值 |
4、实例3 while 99 乘法表
|
3、Shell 循环 until 语句
while循环的条件测试是测真值, until循环则是测假值。
1、 until 循环的语法结构
|
2、 until 语法说明
until 条件测试结果为假 (传回值不为0) , 就进入循环。
条件测试不满足, 执行命令区域。直到 until 条件满足, 结束执行until 循环 (如果条件一直不满足则执行无穷 循环) 。
3、实例1 until 单层条件测试
| # 声明i和sum为整数型 # 条件测试: 只要i值未超过10, 就进入循环 # sum+=i和sum=sum+i是一样的, sum累加上i # i的值递增1, 此行是改变条件测试的命令, 一旦i大于10, 可终止循环 # 遇到done, 回到 until条件测试 # 直到 until 的条件满足显示sum的值 |
4、实例2 until 双层条件测试
4、Shell 循环控制
1、Shell 循环控制说明
break, continue, exit 一般用于循环结构中控制循环的走向。
命令 | 说明 |
break n | n 表示跳出循环的次数, 如果省略 n 表示跳出整个循环 |
continue n | n 表示退到第n层继续循环, 如果省略n表示跳过本次循环进入下一次循环 |
exit n | 退出当前的shell程序, 并返回 n, n 也可以省略 |
return | 用于返回一个退出值给调用的函数 |
shift | 用于将参数列表list左移指定次数, 最左端的那个参数就从列表中删除, 其后边的参数继续进 入循环 |
2、 break 指令
break[N]: 提前结束第N层循环, 最内层为第1层
|
continue: 提前结束本次循环, 提前进入下一轮循环, continue 2 跳出本次内层循环, 进入外层循环
break: 结束本次循环 (整个) , 退出脚本
实例
|
3、continue 指令
continue [N]: 提前结束第N层的本轮循环, 而直接进入下一轮判断; 最内层为第1层
实例
|
4、exit 指令
实例
5、shift 指令
shift 命令用于将参数列表 list 左移指定次数, 最左端的那个参数就从列表中删除, 其后边的参数继续进入循 环。
shift[N]: 用于将参量列表 list 左移指定次数, 缺省为左移一次。
参量列表 list 一旦被移动, 最左端的那个参数就从列表中删 除。while 循环遍历位置参量列表时, 常用到 shift
实例
1、shift 指令实例: 创建指定的多个用户
实例
|
2、运行结果
|
分析: 如果没有输入参数 (参数的总数为0) , 提示错误并退出; 反之, 进入循环; 若第一个参数不为空字符, 则 创建以第一个参数为名的用户, 并移除第一个参数, 将紧跟的参数左移作为第一个参数, 直到没有第一个参数, 退 出。
3、打印直角三角形的字符
实例
|
|
三、Shell 编程之函数
Shell 函数的本质是一段可以重复使用的脚本代码, 这段代码被提前编写好了, 放在了指定的位置, 使用时直接调 取即可
1、定义函数
可以带function fun() 定义, 也可以直接fun() 定义,不带任何参数。
|
function 是 Shell 中的关键字, 专门用来定义函数;
name 是函数名;
commands 是函数要执行的代码, 也就是一组语句;
return value 表示函数的返回值, 其中 return 是 Shell 关键字, 专门用在函数中返回一个值; 这一部分可以 写也可以不写。
由 { } 包围的部分称为函数体, 调用一个函数, 实际上就是执行函数体中的代码。
函数的优势
方便n次使用, 减少代码量, 使之方便, 整洁。
当需要修改里面的重复代码时, 只需要修改一次函数即可实现需求;
将函数写进文件, 需要时直接通过文件调用
2、调用函数
1、执行不带参数的函数
直接输入函数名即可, 不需要带括号,
执行函数时, 函数名前的关键字function和函数名后面的()均不需要带
函数的定义必须要在执行的程序前定义或加载
2、执行带参数的函数
|
|
?/$@)均可以做为函数的参数行传递
$0比较特殊, 仍然是父脚本的名称
此时父脚本的参数会临时被函数的参数所掩盖或隐藏
函数的参数变量是在函数体内里面进行定义
3、 函数的执行总结
Shell各种程序的执行顺序为: 系统别名->函数->系统命令->可执行文件等
函数执行时, 会和调用它的脚本共享变量, 也可以为函数设定局部变量及特殊位置参数 在Shell函数里面, return和exit功能类似, 区别是return是退出函数, exit则是退出脚本 return语句会返回一个值给调用函数的程序, exit则会返回一个值给执行当前脚本的Shell 如果将函数单独存放为一个文件, 在加载时需要使用source或 . 进行加载
在函数内部一般使用local定义局部变量, 仅在函数体内有效
4、调用函数
|
5、从文件中调用函数
3、函数参数传递
|
4、return 返回函数结果
在该示例中, 主要通过 $? 获取返回值, 但返回值的范围只能是 0~255
5、echo 返回函数结果
|
在该示例中, 主要使用 $() 获取返回值, 在该方法中, 没有范围限制, 是一种比较安全的返回方式。
6、全局变量和局部变量
全局变量在shell 脚本中任何地方都能使用; 局部变量在函数内部使用, 声明前加一个 local 就好
|
7、数组变量和函数
$@ 变量会单独处理每个参数
[[email protected] ~]# cat test4.sh
function addarray() {
local sum=0
local newarray
newarray=($(echo "$@"))
for value in ${newarray[*]}
do
sum=$[ $sum + $value ]
done
echo $sum
}
myarray=(1 2 3 4 5)
# 这里 arg1=${myarray[*]} 也可以
arg1=$(echo ${myarray[*]})
result=$(addarray $arg1)
echo "The result is $result"
[[email protected] ~]# bash test4.sh
The result is 15
[[email protected] ~]# cat test5.sh
function arraydblr() {
local origarray
local newarray
local elements
local i
origarray=($(echo "$@"))
newarray=($(echo "$@"))
elements=$[ $# ‐1 ]
for (( i = 0; i <= $elements; i++))
{
newarray[$i]=$[ ${origarray[$i]} * 2 ]
}
echo ${newarray[*]}
}
myarray=(1 2 3 4 5)
arg1=$(echo ${myarray[*]})
result=($(arraydblr $arg1))
echo "The new array is: ${result[*]}"
[[email protected] ~]# cat test4.sh bash test5.sh
The new array is: 2 4 6 8 10
8、递归函数
[[email protected] ~]# cat test6.sh
function factorial() {
if [ $1 ‐eq 1 ]
then
echo 1
else
local temp=$[ $1 ‐ 1 ]
local result=$(factorial $temp)
echo $[ $result * $1 ]
fi
}
read ‐p "Enter value: " value
result=$(factorial $value)
echo "The factorial of $value is: $result"
[[email protected] ~]# bash test6.sh
Enter value: 5
The factorial of 5 is: 120