shell脚本
if分支结构
1) 格式
C语言:
if(条件1)
{
执行的语句
}
else if()
{}
else
{}
shell中的if语句
[]本质上一个test指令,shel中if的条件由test指令完成
# 单分支结构
if [ test语句 ] ----> test 表达式
then
语句块
fi
# 双分支结构
if [ test语句 ] ----> test 表达式
then
语句块
else
不满足
fi
# 多分支结构
if [ test语句1 ] ----> if test 表达式 ---> if [ 表达式 ]; then
then
满足语句1执行的语句块
elif [ test语句2 ] ----> elif test 表达式 ---> elif test 表达式 ; then
then
满足语句2执行的语句块
else
都上都不满足
fi
# 类似于C语言的格式
if (( a == b ))
then
fi
2) if对整数进行判断
$?:获取上一个指令是否是正确的执行结果
0:真 非0:假
a -eq b 测试a和b是否相等 # equal
test $a -eq $b
echo $? # 如果两个数相等则为真,反之为假
a -ne b 测试a和b是否不相等 # on equal
a -gt b 测试 a > b # greater than
a -ge b 测试 a >= b # greater equal than
a -lt b 测试 a < b # less than
a -le b 测试 a <= b # less equal than
read num1 num2
if [ $num1 -eq $num2 ]
then
echo "num1 == num2"
elif test $num1 -gt $num2 # [ $num1 -gt $num2 ]
then
echo "num1 > num2"
else
echo "num1 < num2"
fi
3) if对字符串进行判断
如果使用 [] 代替test进行表达式的处理,在[]内,字符串需要加 ""(获取变量时)
s1 = s2 测试两个字符串内容是否完全一致
test "hello" = "hello"
echo $? # 0 相等为真,不相等为假
s1 != s2 测试两个字符串是否内容有差异 # 相等为假,不相等为真
-z s1 测试s1字符串是否为空
test -z ""
echo $? # 0 字符串没有长度,则为真
test -z "hello"
echo $? # 1 字符串由长度,则为假
-n s1 测试s1字符串是否不为空 # 字符串没有内容,则为假,反之为真
1、终端输入年份,判断闰平年
#!/bin/bash
read -p "请输入一个年份" year
# 判断平闰年
#if [ $((year%4)) -eq 0 -a $((year%100)) -ne 0 -o $((year%400)) -eq 0 ] # 在一个test表达式中,使用 -a判断逻辑与,-o判断逻辑或
#if (( (year % 4 == 0 && year % 100 != 0) || year % 400 == 0 )) # 使用C语言格式进行书写
if [ $((year%4)) -eq 0 ] && [ $((year%100)) -ne 0 ] || [ $((year%400)) -eq 0 ]
then
echo "$year是闰年"
else
echo "$year是平年"
fi
4) if对文件的判断
-d name 测试 name 是否为一个目录
read FILE
test -d $FILE
echo $? # 如果 name 是目录则为真,反之为假
-f name 测试 name 是否为一个普通文件 对软连接文件的判断也会成立
-e name 测试文件是否存在
-b name 测试 name 是否为一个块设备文件
-c name 测试 name 是否为一个字符设备文件
-h name 或者 -L name 测试 name 是否为一个软链接文件
-p name 测试 name 是否为一个管道文件
-S name 测试 name 是否为一个套接字文件
file1 -nt file2 测试1比2的时间戳更新
file1 -ot file2 测试1比2的时间戳更旧
-s name 测试文件大小是否不为0
-w name 测试文件是否存在可写权限
-r name 测试文件是否存在可读权限
-x name 测试文件是否存在可执行权限
练习:
1、在当前目录下,创建一个软链接文件,判断该软链接文件,是否是一个普通文件,如果是普通文件,判断是否有可执行权限。
#!/bin/bash
ln -s ./if.sh ./linkif.sh
filename=./linkif.sh
if [ -f "$filename" ]
then
echo "$filename 是一个普通文件" # 会打印输出,因为打开软链接文件实际上打开的是源文件
if [ -x "$filename" ]
then
echo "有可执行权限"
fi
fi
2、终端输入一个.c文件的文件名,判断文件是否存在,如果存在,判断文件是否有内容,如果有内容,编译该文件。
#!/bin/bash
read -p "请输入一个.c文件:" filename
if [ -e "$filename" ]
then
if [ -s "$filename" ]
then
gcc $filename
else
echo "文件为空"
fi
else
echo "$filename不存在"
fi
case...in分支结构
1) 复习C中switch...case
switch(变量)
{
case 1:
语句;
break;
default:
语句;
}
2) case...in格式
case $变量名 in
常量1)
语句
;; ---> 和c中break的作用相同,shell中必须写;;
常量2)
语句
;;
常量3)
语句
;;
*) ---> 起到通配符的作用,可以匹配所有情况,shell中 *) 必须是最后一条分支
语句 ---> 最后一个分支可以不写;;
esac
case...in中常用可能出现的情况:
1. 1): 如果变量的值为1,和这个分支匹配
2. 1|2|3|4): 当变量的值为1或2或3或4,都会走这个分支
3. [0-9]): 变量的值是0-9的范围内都走该分支 不能写两位数,都是对单个字符判断
4. 1?): 1开头的两个字符的情况
代码示例:
#!/bin/bash
read -p "请输入一个数字:" num
case $num in
1|2|3|4)
echo "1|2|3|4: $num"
;;
2?)
echo "2?: $num"
;;
[5-9])
echo "[5-9]:$num"
;;
3[0-9] | 40)
echo "大于等于30 $num"
;;
*)
echo "其他情况"
esac
1、终端输入一个学生成绩,根据成绩高低分类,[100,90]A,[90,80]B,[80,70]C,[70,60]D,[60,0]不及格
#!/bin/bash
read -p "请输入分数" score
case $score in
9[0-9] | 100)
echo A
;;
8[0-9])
echo B
;;
7?)
echo C
;;
6[0-9])
echo D
*)
echo "不及格"
esac
2、终端输入一个字母,判断是大写字母还是小写字母
#!/bin/bash
read -p "请输入字母" str
case $str in
[[:lower:]])
echo "小写"
;;
[[:upper:]])
echo "大写"
*)
echo "输入错误"
esac
while循环结构
1) 格式
while [ test语句 ] ---> while test 表达式
do
语句块
done
代码示例:
#!/bin/bash
i=1
while [ $i -le 5 ]
do
echo "hello world"
((i++))
done
2) while嵌套格式
while [ test语句 ]
do
while [ test语句 ]
do
语句块
done
done
1、求1-100的和
#!/bin/bash
i=0
sum=0
while [ $i -le 100 ]
do
((sum+=i))
((i++))
done
echo $sum
2、终端输入数组,求数组中元素的和
#/bin/bash
read -a arr # 终端输入的数组一定不是一个稀疏数组
len=${#arr[*]]} # 数组长度
i=0 # 数组下标
sum=0 # 数组元素和
while [ $i -lt $len ]
do
((sum+=${arr[i]}))
((i++))
done
echo "数组和为: $sum"
3、终端输入行数,打印直角三角形
*
**
***
·····
#/bin/bash
read -p "请输入行数" line
i=1 // 行数
while [ $i -le line ]
do
j=1
while [ $j -le $i ]
do
echo -n "*"
((j++))
done
echo
((i++))
done
for循环结构
1) 格式
1. 类似于C语言的格式
for ((表达式1;表达式2;表达式3))
do
语句
done
2. shell自己的格式
for 变量名 in 字符串列表
do
语句
done
for i in aa bb cc dd
do
echo $i
done
执行逻辑:
循环变量i从in后面提供的字符串列表中,按顺序取值,
循环次数和字符串列表中字符串的个数有关,
直到i把字符串列表中每一个字符串的值都获取结束后,循环结束
3. 变量i从命令行中取值,省略in、单词表
for i
do
echo $i
done
2) 使用连续列表的情况
1. seq 起始值 间隔值 终止值 ---> 会将结果回显到终端的指令
如果想在for中使用这个指令,`seq 1 1 100` ---> in 后面就是1-100的列表
2. {1..3} ---> 遍历1-3中的每一个数,
注意:{}中间是两个点,写三个点就失效了
1、终端输入行数,打印直角三角形
*
**
***
·····
#!/bin/bash
read -p "请输入行数" line
for (( ))
do
for (( ))
do
echo
done
echo
done
2、求100-1000内的水仙花数
#!/bin/bash
sum=0
for (())
do
if [ ]
then
echo
fi
done
3、使用for循环,统计家目录下普通文件和目录文件的个数,并输出
#!/bin/bash
dir_count=0
file_count=0
for filename in
do
if [ ]
then
elif [ ]
then
fi
done
echo $dir_count
echo $file_count
select..in语句
1) 格式
select 变量名 in 选项列表
do
语句块
done
select...in如何退出,可以直接ctrl c,或者在代码中运行exit
执行逻辑:
会在终端打印选项列表,用户需要根据选项前面的序号做出选择,选择后变量会获取到相应的值
如果用户不做选择,会再次打印选项列表,
如果用户选错,获取到空值
2) 和case...in结合
#!/bin/bash
select sub in 数学 英语 物理 化学 体育 Q
do
case $sub in
"数学")
echo "上数学"
;;
"英语")
echo "上英语"
;;
"物理")
echo "上英语"
;;
"化学")
echo "上英语"
;;
"q")
exit
;;
esac
done
辅助控制关键字
1) break
break 1; ---> 退出1层循环(1可以不写)
break n; ---> 退出n层循环
2) continue
continue 1 ---> 退出1层本次循环
continue n ---> 退出n层本次循环,跳出n层循环时,只需看第n层循环是否有下一次即可
代码示例:
for ((i=0; i <5;i++))
do
for ((j=0;j<5;j++))
do
if [ $j -eq 3 ]
then
# break
# break 2
# continue
continue 2
fi
echo "$i:$j"
done
done
shell中的函数
1) 格式
function 函数名() ---> function可以不写
{
函数体
return 返回值 ---> 函数是否有返回值,根据实际的实现来决定(0-255之间)
}
注意事项:
1、function在定义函数时,可以加也可以不加
2、shell中的函数是否有返回值,根据实际情况决定
3、shell中函数是否有参数根据实际的调用情况决定
4、shell中的函数仍然满足先定义,后调用的原则
5、()内必须为空,不能写任何内容
fun()
{
}
fun 1 2 3
2) 调用函数
直接通过函数名调用函数
函数名
如果函数需要外部传参:
函数名 参数1 参数2 参数3 ·····
3) 如何获取函数的外部参数
仍然通过位置变量获取:
$0 ---> 获取的是脚本名
$n ---> n的数值在10及以上需要加 ${}
脚本和函数的外部参数都通过位置变量获取,互不影响
1. 在函数内使用位置变量,获取的是函数的外部参数
2. 在脚本中使用位置变量,获取的是脚本的外部参数
3. 如果想要在函数中,使用脚本的外部参数,只能通过函数调用将该参数传给函数
代码示例:
#!/bin/bash
function fun()
{
num=10
sum=$(($1+$2))
}
echo $sum
echo $num # 空
fun 11 12 # 调用函数fun并传了两个参数到函数中
echo $sum # 在函数外可以访问函数内的变量,shell中变量全部都是全局变量
echo $num # 10, 因为调用过函数了num赋值过
4) 如何获取函数的返回值
shell中函数返回值通过 $? 获取,返回值只能是0-255范围内的数
$?获取返回返回值的本质:
$?是获取上一条指令的执行结果
#!/bin/bash
function fun()
{
return 30
}
fun
ret=$?
echo ret=$ret
ret=fun # 给ret变量赋值为fun字符串,并没有调用fun函数
5) 接收无返回值函数的结果
#!/bin/bash
fun()
{
echo hello world
}
ret=`fun` # 没有hello world输出在终端,因为echo的输出被命令置换符获取了,并且赋值给了变量
echo $ret
6) local在函数中的使用
修饰变量的关键字local:定义局部变量
function fun()
{
local num=10
sum=$(($1+$2))
}
fun 11 12
echo $sum
echo $num # 空值,因为num是一个局部变量,不能在函数外被访问
c_count=0
# 遍历家目录下的所有文件和目录
for filename in ~/*; do
if [ -c "$filename" ]; then
((c_count++)) # 增加字符设备文件计数
fi
done
# 输出结果
echo "家目录下,c文件的个数为:$c_count"
declare -A arr=(
[2]=9
[4]=8
[30]=23
[24]=3
[21]=7
)
# 函数计算稀疏数组的和
function sum_sparse_array() {
declare -n input_array=$1 # 使用declare -n创建一个引用
local sum=0
# 遍历传入的稀疏数组
for index in "${!input_array[@]}"; do
((sum += input_array[$index])) # 累加每个元素的值
done
echo $sum # 返回和
}
# 调用函数并输出结果
result=$(sum_sparse_array arr) # 将数组名作为参数传递
echo "稀疏数组的和为: $result"