shell入门下篇
文章目录
一、shell中的算数运算
- shell本身不擅长进行算数运算,shell更加擅长的是命令的执行
以及文件的处理,文件的判断。 - 由于shell中定义的变量的值默认都当成字符串处理,因此在使用
shell进行算数运算时,一般需要借助特定的算术运算的符号或着shell命令。
(()) $[]
语法格式
1. 语法格式
((算数表达式1, 算数表达式2, 算数表达式3, .....))
2. 特点
2.1> (())中可以跟多个不同的算数表达式,可以进行复杂的算数运行,
(())中的每个算数表达式都会被执行。
2.2> 获取(())运算符中算数表达式的结果
((retVal1=算数表达式1, retVal2=算数表达式2, retVal3=算数表达式3, .....))
---> 获取(())中每个算数表达式的结果
retVal=$((算数表达式1, 算数表达式2, 算数表达式3, .....))
---> 最后一个算数表达式的结果被返回
2.3> 在算数表达式中,如果出现变量之后,变量名前可以加$,也可以不加$
代码如下(示例):
#!/bin/bash
# your code
echo "*** 1. (())中可以书写多个复杂的算数表达式 ***"
((1+2+3, 4+5*6, 1 && 2 || 0))
echo "*** 2. 获取(())中表达式的结果 *** "
# 获取(())每个表达式的结果
((retVal1=100+200+300, retVal2=4+5*6, retVal3=1 && 2 || 0))
echo "retVal1 = $retVal1"
echo "retVal2 = $retVal2"
echo "retVal3 = $retVal3"
# 获取(())最后一个表达式的结果
retVal=$((1+2+3, 10-8/2, 1024 % 100 / 2))
echo "retVal = $retVal"
echo ''*** 3. 如果(())使用了变量,变量名前可以加$,也可以不加$'
# read : 从终端读取数据到变量中
# -p "提示字符串" : 在读入数据之前,将提示字符串在终端回显
read -p "请输入两个整数值 > " a b
sum1=$((a + b))
sum2=$(($a + $b))
echo "sum1 = $sum1"
echo "sum2 = $sum2"
二、if…else…分支语句
语法格式
1. if分支语句
if ((条件表达式1)) ---> if和then写到一行 if ((条件表达式1)) ; then
then
shell语句
fi
执行过程:“条件表达式1”成立,则执行“shell语句”;
“条件表达式1”不成立,则执行if后边的语句。
2. if...else...分支语句
if ((条件表达式1)) ---> if和then写到一行 if ((条件表达式1)) ; then
then
shell语句1
else
shell语句2
fi
执行过程:“条件表达式1”成立,则执行“shell语句1”;
“条件表达式1”不成立,则执行“shell语句2”。
3. if..elif...else...分支语句
if ((条件表达式1)) ---> if和then写到一行 if ((条件表达式1)) ; then
then
shell语句1
elif ((条件表达式2)) ---> elif和then写到一行 if ((条件表达式1)) ; then
then
shell语句2
elif ((条件表达式3)) ---> elif和then写到一行 if ((条件表达式1)) ; then
then
shell语句3
.....
else
shell语句n
fi
执行过程:判断“条件表达式1”是否成立,如果成立则执行“shell语句1”,
如果不成立,则继续判断后边的“条件表达式”是否成立,如果成立则执行对应的
“shell语句”,如果不成立,则继续判断后边的“条件表达式”是否成立,依次类推,
只有前边的所有的条件表达式都不成立时,才会执行else分支中的“shell语句”。
注:只要前边有一个条件表达式成立,则执行对应的shell语句,
后边的所有的条件表达式将都不会在进行判断,即使条件表达式成立。
4. if分支语句的嵌套
if ((条件表达式1))
then
if ((条件表达式1_1))
then
shell语句1_1
else
shell语句1_2
fi
else
shell语句2
fi
代码如下(示例):
案例1:从终端输入百分制的成绩,然后判断成立的等级:
A(优秀) B(良好) C(一般) D(及格) E(差)
#!/bin/bash
# your code
read -p "请从终端输入成绩(0-100) > " score
if ((score < 0 || score > 100))
then
echo "输入的成绩不合理,请重新执行脚本文件"
exit
fi
if ((score >= 90)) ; then
echo "A"
elif ((score >= 80)) ; then
echo "B"
elif ((score >= 70)) ; then
echo "C"
elif ((score >= 60)) ; then
echo "D"
else
echo "E"
fi
三、case…in分支语句
语法格式
1. 语法格式
case $变量名/$((算数表达式)) in ---> switch(常量表达式)
常量1) ---> case 常量1:
shell语句1 ---> C语句
;; ---> break
常量2)
shell语句2
;;
常量3)
shell语句2
;;
......
常量n)
shell语句n
;;
*) --->default:
shell语句n+1
;;
esac
2. 注意事项
1> 每个分支中的两个分号不可以省略,除了最后一个分支的两个分号可以省略。
3. 常量
hello) ---> 字符串常量
Y|YES|Yes|yes) ---> 任意一个字符串
1|2|3|4) ---> 匹配数组的1 2 3 4的任意一个
[1-9]) ---> 匹配数字1-9的任意一个
[a-zA-Z]) ---> 匹配字符a-z或A-Z中的任意一个字符
[abcd]) ---> 匹配abcd中的任意一个字符
[^abcd]) ---> 匹配除了abcd中的任意一个字符
代码如下(示例):
案例1:使用case...in分支语句,实现成绩的分类
#!/bin/bash
# your code
read -p "请从终端输入成绩(0-100) > " score
if (($score < 0 || $score > 100))
then
echo "输入的成绩不合理,请重新执行脚本文件"
exit
fi
case $((score / 10)) in
9|10)
echo "A"
;;
8)
echo "B"
;;
7)
echo "C"
;;
6)
echo "D"
;;
*)
echo "E"
;;
esac
四、test命令
4.1命令介绍
1. 可以使用man test命令查看test的帮助手册
2. 功能:检查文件的类型或比较值
test命令可以用于逻辑判断,字符串的比较,整数的比较,文件类型的判断
文件读写可执行权限的判断,文件的比较。
3. 用法:
test 表达式
或者
[ 表达式 ] ---> 最终调用的是test命令
4. test命令经常和if...else分支语句配合使用,
完成逻辑判断,字符串的比较,整数比较,文件类型的判断,
文件权限判断,文件的比较。
4.2 test命令和if…else分支语句结合的语法格式
1. if分支语句
if [ 表达式 ] ; then
shell语句
fi
-------------------------
if test 表达式 ; then
shell语句
fi
2. if...else分支语句
if [ 条件表达式 ] ; then
shell语句1
else
shell语句2
fi
-------------------------
if test 条件表达式 ; then
shell语句1
else
shell语句2
fi
3. if ... elif ...else分支语句
if [ 条件表达式 ] ; then
shell语句1
elif [ 条件表达式2 ] ; then
shell语句2
......
else
shell语句n
fi
--------------------------
if test 条件表达式 ; then
shell语句1
elif test 条件表达式2 ; then
shell语句2
......
else
shell语句n
fi
4. test命令和[]使用时,对于表达式的格式要求
1> [] 前后必须有空格
if ["hello" > "world"] ;then ---> Error
if [ "hello" > "world" ] ;then ---> Ok
2> 参数,变量,运算符,前后也必须有空格
if [ "hello">"world" ] ;then ---> Error
if [ "hello" > "world" ] ;then ---> Ok
if [ $a-a$b ] ; then ---> Error
if [ $a -a $b ] ; then ---> Ok
3> 如果使用了变量,变量名前必须加$, $不可以省略
使用变量时,最好将变量使用双引号括起来。
问题:为什么test命令的参数之间必须有空格?
test本质是一个shell命令,及是一个ELF格式的可执行程序。
test $a -a $b ---> OK
test $a-a$b ---> Error
test命令中的main函数的argc和argv会获取参数,并对应参数进行
解析,如果每个参数之间有空格,会被解析成多个参数。
如果每个擦拭农户之间没有空格,会被解析成一个参数,此参数可能无法识别。
4.3 使用test命令进行逻辑判断
-a ---> 逻辑与运算(&&)
-o ---> 逻辑或运算(||)
! ---> 逻辑非运算(!)
[ 表达式1 -a 表达式2 ] <==> test 表达式1 -a 表达式2
[ 表达式1 -o 表达式2 ] <==> test 表达式1 -o 表达式2
[ ! 表达式 ] <==> test ! 表达式
---> 判断表达式为假
[ 表达式 ] <==> test 表达式
---> 判断表达式为真
4.4 使用test命令进行字符串的判断和比较
字符串对象(⼀定要注意在进⾏字符串的判断的时候都需要加上"","$a" "hello")
-z 判断字符串是否为空(零) ----> 为空返回真,非空返回假
-n 判断字符串是否为⾮空(零) ---> 非空为真,空为假
=或== 都是⽤来判读字符串是否相等 ---> 成立返回真,不成立返回假
!= 不等于 ---> 成立返回真,不成立返回假
\> ⼤于 (防⽌误认为重定向) ---> 成立返回真,不成立返回假
\< ⼩于 ---> 成立返回真,不成立返回假
\>= 大于等于
\<= 小于等于
表达式的格式:
[ -z STRING ] <====> test -z STRING
[ STRING1 != STRING2] <====> test STRING1 != STRING2
代码如下(示例):
从终端输入两个字符串,比较两个字符串的大小。
#!/bin/bash
# your code
read -p "请输入两个字符串 > " s1 s2
# 如果test命令中的多个表达式进行逻辑运算使用-a -o
# if [ \( -n "$s1" \) -a \( -n "$s2" \) ] ; then
# echo "s1和s2字符串都不为空"
# else
# echo "s1和s2字符串至少有一个为空"
# exit
# fi
# 如果多个test命令的结果进行逻辑运算使用 && ||
# if [ -n "$s1" ] && [ -n "$s2" ] ; then
# echo "s1和s2字符串都不为空"
# else
# echo "s1和s2字符串至少有一个为空"
# exit
# fi
if [ -z "$s1" -o -z "$s2" ] ; then
echo "s1和s2字符串至少有一个为空"
exit
else
echo "s1和s2字符串都不为空"
fi
if test "$s1" == "$s2" ; then
echo "s1 == s2 字符串"
elif test "$s1" \> "$s2" ; then
echo "s1 > s2 字符串"
else
echo "s1 < s2 字符串"
fi
4.5 使用test命令进行整数的比较
-eq : 相等
-ne : 不相等
-gt : 大于
-ge : 大于等于
-lt : 小于
-le : 小于等于
表达式的格式:
[ INTEGER1 -eq INTEGER2 ] <=等价于=> test INTEGER1 -eq INTEGER2
代码如下(示例):
案例:成绩的分类
#!/bin/bash
# your code
read -p "请从终端输入成绩(0-100) > " score
if [ "$score" -lt 0 -o "$score" -gt 100 ]
then
echo "输入的成绩不合理,请重新执行脚本文件"
exit
fi
if [ "$score" -ge 90 ] ; then
echo "A"
elif [ "$score" -ge 80 ] ; then
echo "B"
elif [ "$score" -ge 70 ] ; then
echo "C"
elif [ "$score" -ge 60 ] ; then
echo "D"
else
echo "E"
fi
4.6 使用test命令进行文件的比较
filename1 -nt filename2 : 判断filename1文件比filename2文件的时间新
filename1 -ot filename2 : 判断filename1文件比filename2文件的时间旧
filename1 -ef filename2 : 判断filename1文件比filename2文件的inode是否⼀致
表达式的格式:
[ filename1 -nt filename2 ] <====> test filename1 -nt filename2
4.7 使用test命令进行文件类型的判断
-b filename 判断⽂件是否存在,是否是块设备
-c filename 判断⽂件是否存在,是否是字符设备
-d filename 判断⽂件是否存在,是否是⽬录
-f filename 判断⽂件是否存在,是否是普通⽂件
-p filename 判断⽂件是否存在,是否是管道⽂件
-L filename 判断⽂件是否存在,是否是链接⽂件 (经测试链接⽂件也是普通⽂件)
-S filename 判断⽂件是否存在,是否是套接字⽂件
-e filename 判断⽂件是否存在
-s filename 判断⽂件是否存在,判断⽂件是否为空
表达式的格式:
[ -b FILENAME ] <===> test -b FILENAME
4.8 使用test命令进行文件权限的判断
-r filename 判断⽂件是否存在,是否有可读权限
-w filename 判断⽂件是否存在,是否有可写权限
-x filename 判断⽂件是否存在,是否有可执⾏权限
表达式的格式:
[ -r FILENAME ] <====> test -r FILENAME
代码如下(示例):
案例:
执行脚本文件时,给脚本文件传递一个文件名字,
判断此文件是普通文件还是目录,
如果是普通文件则判断文件是否为脚本文件,
如果为脚本文件则判断文件是否具有可执行的权限,
如果没有可执行的权限则添加可执行的权限
提示:
执行脚本文件时,传递文件的方式
bash ***.sh filename
脚本文件中获取文件名的方式:$1
判断脚本文件的方式,判断文件的后缀。
prefix=`echo $1 | cut -d "." -f 2`
if [ "$prefix" = "sh" ]
#!/bin/bash
# your code
if [ -f "$1" ] ; then
echo "$1文件存在,并且为一个普通的文件"
prefix=`echo $1 | cut -d "." -f 2`
if [ "$prefix" == "sh" ] ;then
echo "$1文件是一个脚本文件"
# if [ -x "$1" ] ; then
# echo "$1文件具有可执行权限"
# else
# echo "$1文件不具有可执行权限"
# chmod a+x $1
# echo "$1文件添加可执行权限成功"
# fi
if [ ! -x "$1" ] ; then
echo "$1文件不具有可执行权限"
chmod a+x $1
echo "$1文件添加可执行权限成功"
else
echo "$1文件具有可执行权限"
fi
else
echo "$1文件不是一个脚本文件"
fi
elif [ -d "$1" ] ; then
echo "$1文件存在,并且为一个目录文件"
else
echo "$1文件既不是普通文件,也不是目录"
fi
五、select…in选择分支语句
语法格式
1. 语法格式
select 变量名 in 单词列表
do
shell语句
done
2. 单词列表格式
多个单词构成,单词之间使用空格隔开。
比如: one two three four five
3. 执行过程:
每次从单词列表中选择一个单词赋值给"变量名",
然后“shell语句”执行一次。
4. select...in选择分支语句经常和case...in配合使用,
提高代码的交互性。
代码如下(示例):
#!/bin/bash
# your code
select number in one two three four five
do
echo "你的选择为$number"
done
------------------------------------------------
linux@ubuntu:~/DC23101/day04$ bash 08select.sh
1) one
2) two
3) three
4) four
5) five
#? 1 ---> 只可以输入菜单对应的编号
你的选择为one
#? 4
你的选择为four
#? 3
你的选择为three
#? one ---> 输入字符串无效
你的选择为
#? 6 ---> 输入不存在的编号也无效
你的选择为
#? ---> 不输入内容,直接回车则重新显示菜单
1) one
2) two
3) three
4) four
5) five
#? ^C
#!/bin/bash
# your code
select OS in macOS Windows Ubuntu Android IOS
do
case $OS in
"macOS")
echo "打开macOS系统"
;;
"Windows")
echo "打开Windows系统"
;;
"Ubuntu")
echo "打开ubuntu系统"
;;
"Android")
echo "打开Android系统"
;;
"IOS")
echo "打开IOS系统"
;;
*)
echo "打开的系统不存在"
;;
esac
done
#!/bin/bash
# your code
if [ "$#" -ne 1 ] ; then
echo "usage : bash $0 fileName"
exit
else
if [ "$1" == "help" ] ; then
echo "usage : bash $0 fileName"
exit
elif [ -f "$1" ] ; then
echo "文件存在,并且为普通文件"
fi
fi
select cmd in cat head tail file
do
case $cmd in
"cat")
cat $1
;;
"head")
head $1
;;
"tail")
tail $1
;;
"file")
file $1
;;
*)
echo "不支持的命令操作"
;;
esac
done
六、for循环语句
语法格式
1. 类似于C语言的语法格式
for ((表达式1; 表达式2; 表达式3))
do
shell语句4
done
执行过程:[1,2][4,3,2][4,3,2][4,3,2]......
2. 类似于python语言的语法格式
for 变量名 in 单词列表
do
shell语句
done
执行过程:依次从单词列表中取出一个单词,然后赋值给"变量名",
循环体中的"shell语句"执行一次。直到所有的单词列表都赋值完之后,
循环结束。
举例:
for num in 1 2 3 4 5 6 7 8
do
done
循环8次。
----------
for num in one two three four five
do
done
循环5次
代码如下(示例):
案例1:定义一个数组,数组的长度为10,数组的每个元素的值都是要给整数。
求数组中每个元素相加之和。
#!/bin/bash
# your code
arr=(10 20 30 40 50 60 70 80 90 100)
sum=0
# ${#arr[*]} : 统计数组中的元素个数之和
for ((i = 0; i < ${#arr[*]}; i++))
do
((sum += ${arr[i]}))
done
echo sum = $sum
###############################################
sum=0
for index in 0 1 2 3 4 5 6 7 8 9
do
((sum += ${arr[$index]}))
done
echo sum = $sum
################################################
sum=0
for val in ${arr[*]}
do
((sum += val))
done
echo sum = $sum
##########################################
# seq命令,可以输出一个等差数列
# seq 起始值 终止值 ---> 相邻的两个数默认相差1
# seq 起始值 相邻差值 终止值 ---> 相邻的两个数相差"相邻差值"值
sum=0
for index in `seq 0 1 9`
do
((sum += ${arr[$index]}))
done
echo sum = $sum
案例2:for循环的嵌套, 使用for循环打印以下图形:
*
**
***
****
*****
#!/bin/bash
# your code
read -p "请输入图形显示的行数 > " line
for i in `seq 1 $line`
do
for j in `seq 1 $i`
do
# -n :取消换行符
echo -n '*'
done
echo ''
done
案例3:for循环的嵌套, 使用for循环打印以下图形:
*****
****
***
**
*
#!/bin/bash
# your code
read -p "请输入图形显示的行数 > " line
for i in `seq 1 $line`
do
for j in `seq 1 $[line - i + 1]`
do
echo -n '*'
done
echo ''
done
案例4:for循环的嵌套, 使用for循环打印以下图形:
*
**
***
****
*****
#!/bin/bash
# your code
read -p "请输入图形显示的行数 > " line
for i in `seq 1 $line`
do
for j in `seq 1 $[line - i]`
do
echo -n ' '
done
for k in `seq 1 $i`
do
echo -n '*'
done
echo ''
done
案例5:for循环的嵌套, 使用for循环打印以下图形:
*
***
*****
*******
********* line * 2 - 1
#!/bin/bash
# your code
read -p "请输入图形显示的行数 > " line
for i in `seq 1 $line`
do
for j in `seq 1 $[line - i]`
do
echo -n ' '
done
for k in `seq 1 $[i * 2 - 1]`
do
echo -n '*'
done
echo ''
done
案例6:定义一个数组,数组长度为10,并对数组每个元素的值为整数,
使用冒泡排序的方式,对数组中的元素进行排序。
arr=(58 78 45 36 99 106 28 66 75 9)
#!/bin/bash
# your code
arr=(58 78 45 36 99 106 28 66 75 9)
echo "排序之前:${arr[@]}"
# 循环的趟数
for i in `seq 0 $[${#arr[*]} - 2]`
do
# 每趟比较的次数, 比较次数递减
for j in `seq 0 $[${#arr[*]} - 2 - i]`
do
if [ ${arr[j]} -gt ${arr[j+1]} ] ; then
tmp=${arr[j]}
arr[j]=${arr[j+1]}
arr[j+1]=$tmp
fi
done
done
echo "排序之后:${arr[@]}"
七、while循环
语法格式
1. 格式1
while ((条件表达式1))
do
shell语句2
done
执行过程:[1][2,1][2,1][2,1]....
2. 格式2
while [ 条件表达式1 ]
do
shell语句2
done
执行过程:[1][2,1][2,1][2,1]....
3. 格式3
while true
do
shell语句
done
死循环
案例:求100-999之间的水仙花数。
#!/bin/bash
# your code
num=100
while ((num < 1000))
do
g=$[num % 10]
((s=num/10%10))
b=$((num/100))
tmp=$((g*g*g+s*s*s+b*b*b))
if [ $tmp -eq $num ] ; then
echo $num
fi
((num++))
done
echo "--------------------------"
num=100
while [ $num -lt 1000 ]
do
g=$[num % 10]
((s=num/10%10))
b=$((num/100))
tmp=$((g*g*g+s*s*s+b*b*b))
if [ $tmp -eq $num ] ; then
echo $num
fi
((num++))
done
echo "---------------------------"
num=100
while true
do
if [ $num -ge 1000 ] ; then
break
fi
g=$[num % 10]
((s=num/10%10))
b=$((num/100))
tmp=$((g*g*g+s*s*s+b*b*b))
if [ $tmp -eq $num ] ; then
echo $num
fi
((num++))
done
八、break和continue
语法格式
break num
num = 1 时, 退出1层循环, 如果num=1可以省略不写
num > 1 时, 退出num层循环
continue num
num = 1 时, 退出1层本次循环,执行下一次的循环,如果num=1可以省略不写
num > 1 时, 退出num层本次循环,执行下一次的循环。
九、shell函数
9.1语法格式
1. 语法格式
function 函数名()
{
函数体
}
注:
1> shell中的函数的使用依然遵循先定义后使用的原则。
2> shell中的函数是否有返回值,由最终的代码决定。
3> shell中的函数是否有参数,由最终的代码决定。()中不需要写任何的东西
9.2shell函数的调用
1. 函数没有形参,不需要传递实参
函数名
2. 函数有参数,需要传递实参
函数名 实参列表(多个参数之间使用空格隔开)
9.3shell函数的参数
在函数内获取函数传递的实参值,通过位置变量获取
$1 ---> 第1个实参
$2 ---> 第2个实参
.....
${10} ---> 第10个实参
.....
${n} ---> 第n个实参
${*} ---> 所有的实参
${@} ---> 所有的实参
${#} ---> 所有的实参的个数
9.3shell函数的返回值
1. 在shell中默认定义的所有的变量都是全局变量,即使在函数内定义的变量
也是全局变量,因此可以在函数外之间使用函数内定义的变量。
此时函数无需返回。
2. 通过return返回local修饰的局部变量的值,只能返回0-255之间的整数,
通过$?接收return返回的函数值。
3. 通过echo命令返回函数的值,可以返回任意的整数或者字符串。
通过echo命令返回函数的值时,需要使用命令置换符接收函数的返回结果。
变量名=`函数名 实参列表`
变量名=$(函数名 实参列表)
9.3 shell函数案例
#!/bin/bash
# your code
# 1. 定义函数,没有参数,没有返回值
function print()
{
echo "hello world"
}
# 2. 定义函数,有参数,没有返回值
function print_arr()
{
local arr=(${*})
for i in `seq 0 $[${#arr[*]} - 1]`
do
echo -n ${arr[i]}
echo -n ' '
done
echo ''
# 获取所有的参数值
echo ${*}
# 获取参数的个数
echo ${#}
}
# 3. 定义函数,有参数,有返回值,返回值通过全局变量返回
function add_func1()
{
sum=$(($1 + $2))
}
# 4. 定义函数,有参数,有返回值,通过return返回0-255之间的整数
function add_func2()
{
local local_sum=0
local_sum=$[$1 + $2]
return $local_sum
}
# 5. 定义函数,有参数,有返回值,通过echo命令返回
function add_func3()
{
local local_sum=0
local_sum=$(($1 + $2 + $3 + $4))
echo $local_sum
}
# 1. 调用函数,没有参数,没有返回值
print
# 2. 函数的调用,有参数,没有返回值
arr=(aaa bbb ccc ddd eee 111 222 333 444 555)
print_arr ${arr[*]}
# 3. 函数调用,有参数,有返回值,返回值通过全局变量返回
add_func1 100 200
echo sum = $sum
# 4. 函数调用,有参数,有返回值,通过return返回0-255之间的整数
add_func2 10 20
# 函数的返回值通过$?接收
echo 10 + 20 = $?
add_func2 100 200
# 函数的返回值通过$?接收
echo 100 + 200 = $?
# 5. 函数调用,有参数,有返回值,通过echo命令返回
sum=`add_func3 10000 20000 30000 40000`
echo sum = $sum