1、shell 概述
A、是什么?
shell 是一门计算机语言,和 python | Java 一样,都可以编写程序
A-1、shell字面意思: 壳(相对核而言),核:指操作系统,shell 是保护操作系统的
A-2、计算机操作系统只能识别 0 和 1 组成的机器码,现在我们是通过GUI|CLI 来间接操作操作系统
GUI(图形化界面) | CLI(命令行) 在用户与操作系统之间,相当于桥梁、中介的作用,结构上看,GUI和CLI 保护操作系统
A-3、GUI 与 CLI 就是通过 Shell 实现的
B、为什么?
通过shell按照需求编写一些程序
C、怎么用?(linux)
C-1、分类
第一类:GUI样式的shell
第二类:CLI样式的shell(重点)
专指: Linux 下的 shell 编程
C-2、流程
1)、创建一个文本文档,后缀名是 .sh 文档名要做到见名知意
2)、再在文本文件中录入一个命令
echo "xxxx" 在命令行模式下直接输出数据
规范:第一行固定格式,这是解释器声明: #!/bin/bash
作用:指定脚本解析的解释器
3)、执行 .sh 文件(.sh又称为shell 脚本,里面放DOS命令)
方式1: sh(bash) shell文件
方式2: 绝对路径(/xxx/yyy/abc.sh) 或 相对路径(./abc.sh),注意:开启所有权限 chmod 777 abc.sh
方式3: source abc.sh(source 相当于 ./)
D、练习
练习1:在命令行输出当前所在目录:pwd
2、shell 语法: 注释
A、是什么?
注释是程序中非功能性说明文本
B、为什么?
增强程序的易读性,易维护性
C、怎么用?
注释量占到代码总量的 30% -- 50%
单行注释(常用):一次只注释一行
格式: # 注释文本
多行注释(了解,基本不用):一次可以注释多行
:<<自定义的标记
第一行注释
第二行注释
.....
自定义标记
3、shell 语法: 变量
A、是什么?
程序=数据+数据操作
变量就是数据的载体,变量之所以称之为变量,是因为其中的值可以改变
B、为什么?
变量为数据设置一个引用,以后再使用这个数据时,可以通过引用获取数据
C、怎么用?(重点)
变量创建:
格式: 变量名="变量值"
注意1:变量名
a)、不能数字开头
b)、变量名不能有空格这种特殊字符
c)、起名做到见名知意
d)、变量名不要使用关键字
注意2:赋值符号
=左右两侧不要有空格
注意3:变量值
a)、变量值可以不使用引号,但是如果有空格,必须使用 "xxx" 或者 'xxx'
b)、"xxx" 和 'xxx' 的区别,变量之间赋值时,如果是 变量2="${变量1}",那么赋的是变量1的值
如果是 变量2='${变量1}' 只是赋值了变量1的调用格式,就是echo ${变量2} 返回的是${变量1},而不是变量1的值
变量查询:
格式:"${变量名}" ----- 标准格式
注意:其他格式(不建议使用):${变量名} 或者 $变量名
变量修改:
格式:同变量新增
变量删除:
格式:unset 变量名
D、变量分类
D-1、本地(局部)变量
只有当前 shell 可以使用的变量
D-2、全局变量(静态变量)(了解)
被多个 shell 共享的变量
需求:如何将本地变量转换成全局变量
思想:将本地变量设置为全局变量就是要将本地变量导出到共享空间
格式: export 局部变量
查询全局变量: env
注意1:如果是全局变量,建议变量名所有字母都大写
注意2:全局变量要慎用
D-3、内置变量(特殊变量)
需求:编写shell动态获取某个目录下的子级(要获取的目录不一定)
实现流程:
1)、shell 调用时,可以传入要操作的目录
格式: sh abc.sh 某个目录
2)、shell 执行时,可以获取调用传入的目录
格式:ls $1 (代表传入的第一个参数)
上述流程其实就是传参以及参数解析的过程,这个参数就可以称之为内部变量
特殊变量语法总结:
应用场景,程序执行时有些数据是可变的,可以调用脚本时,传入这些可变数据,脚本中解析获取
调用格式: sh xxx.sh 参数1 参数2 参数3 .....
解析格式: $N 获取第 N 个参数
优 点: 动态传值,更灵活
注 意: $N 获取第 N 个参数,但是 N 最大就到 9
$0 获取脚本文件名
$* 获取所有参数
$# 获取参数个数
D-4、扩展:读取键盘录入
需求:编写shell动态获取某个目录下的子级(目录不一定,要让调用者指定)
格式: read_ -p_"提示语句:"_变量名 (注意:_是空格!!!!)
作用:执行到此时,程序挂起,等待用户录入数据,录入数据后,回车,录入的数据会赋值给变量
优点:动态获取数据,更灵活!!!!!
E、变量特殊赋值(记住)
需求:将某个命令的结果赋值给一个变量
格式:变量名=`命令`
F、练习
练习1:使用shell脚本,输出当前所在的目录
知识点:将命令结果赋值给变量
练习2:计算/etc目录下有多少个文件,用shell脚本实现
新知识点:获取某个目录下子级个数
固定格式:ls 目录 | wc -l
练习3:实现统计用户指定目录下的文件个数,用 shell 脚本实现
知识点:怎么动态获取数据?(特殊变量或者读取键盘录入)
4、shell 语法: 运算符
A、是什么?
是变量执行运算时使用的一些特殊符号
B、为什么?
因为程序中频繁使用算数运算、逻辑运算、比较运算....等算法
C、怎么用?
运算符分类
C-1、算数运算符
格式: $((数学表达式))
运算符: + - * / % ----> 加减乘除取余
注意: 一般计算机语言中除法运算只取商
C-2、比较运算符:返回的是 boolean 值(特殊: 0为true 1为false)
格式: [ 表达式 ] ---- 注意:[] 中有两个空格,两个空格中添加表达式
查看结果: $?
运算符: 不能直接使用 > < >= <= == !=
使用对应的参数 -gt(>) -lt(<) -ge(>=) -le(<=) -eq(==) -ne(!=) 记住
g = greater
t = than
l = less
e = equals
n = not
C-3、逻辑运算符:返回 boolean 值(特殊: 0为true 1为false)
格式: [ 表达式 ]
查看结果: $?
运算符: -a 与-o或 !非
C-4、字符串比较:返回 boolean 值
格式: [ 表达式 ]
查看结果: $?
运算符: == 判断两个字符串内容是不是一样
!= 判断两个字符串内容是不是不一样
-z 判断单个字符串长度是不是0(判断字符串是不是空)
C-5、文件判断:返回 boolean 值
格式: [ 表达式 ]
查看结果: $?
运算符:-d: 判断是不是文件夹
-f: 判断是不是文件
-e: 判断是不是存在
变体:
test -参数 路径 等价于 [ -参数 路径 ]
例如:test -d ${myDir} 等价于 [ -d ${myDir} ]
D、练习
练习1:判断 /home/admin(/root)目录是否为空
思路:现获取目录子级文件个数,判断是否大于 0
练习2:通过用户输入任意目录判断是否为空
5、shell 语法: 函数(方法)
A、是什么?
程序=数据+数据操作
变量是数据的载体,函数是数据操作的载体,是程序中最小的功能(封装)单元,在函数中一般会封装一个具有特定功能的实现
B、为什么?
优点1:代码结构更规整,易维护 易读
优点2:实现了功能复用
C、怎么用?
C-1、函数声明
function(可选) 函数名(){
函数体
}
C-2、函数调用
函数名
C-3: 注意
1、函数不调用不执行(所有计算机语言通用)
2、顺序上必须先声明再调用(弱类型计算机语言的特点)
C-4、函数分类
类型1:无参数无返回值
类型2:有参数
声明语法:获取参数使用
$N : 获取第 N 个参数,N必须小于等于 9
$0:脚本文件名
$*:所有参数
$#:参数个数
调用语法:参数传递
函数名 参数1 参数2 .....
如下图:
类型3:有返回值(了解)
声明语法:
函数名(){
....
return xxx;
}
调用语法:
函数名
$? 查看结果
注意:函数的返回值在 shell 中两种:
return 0 : 函数调用正常
非0 : 函数执行有误
在shell中的函数中的变量在大括号外面也能调用
D、练习
练习1:读取键盘录入,录入长方形的长和宽,编写求周长和面积的函数,调用并输出周长和面积的值
6、shell 语法: 流程控制(重点)
A、是什么?
就是控制代码的执行顺序,让代码实现分支或循环执行
B、为什么?
组织多样的业务逻辑
C、分类?
分支: if + case
循环: for + while
7、shell 语法:流程控制分支实现之 if
A-1、格式1(单分支)
语法:
if [ boolean表达式 ]
then
code......
fi
举例:
(需求: 录入年龄,判断是否成人,如果成人了输出"成年人")
A-2、格式2(双分支)
语法:
if [ boolean表达式 ]
then
code.....
else
code.....
fi
举例:
(需求: 录入年龄,判断是否成人,如果成人了输出"成年人",否则输出未成年)
A-3、格式3(多分支)
语法:
if [ boolean表达式 ]
then
code....
elif [ boolean表达式 ]
then
code....
elif 可以出现多次
else
code....
fi
举例:
(需求: 录入年龄,如果小于18岁,输出未成年,如果大于等于18岁小于等于30岁,输出青年,如果大于30岁小于50岁,输出中年,否则,输出老年)
B、练习
练习1:判断用户输入的用户名和密码是否为admin 123456,如果是则提示登录成功,否则提示失败
知识点:if else +运算符
练习2:输入数字,判断是否大于0,如果大于0则将该数字-1并输出,否则+1输出
练习3:判断用户输入的目录是否存在,如果存在则统计目录下的文件个数,否则提示用户该目录不存在
练习4:判断学生的成绩,大于90-100提示优秀,80-90之间提示良好,70-80之间则提示一般,60-70之间提示 及格。其他则提示不及格
练习5:判断用户输入的内容是否为空,为空则提示,不为空则判断是否为目录,不为目录则判断是否为文件,否则提示错误信息
知识点:多分支
8、shell 语法:流程控制分支实现之case(了解)
A、格式
语法:
case 变量(用户输入的数据) in
值1)
code;;
值2)
code;;
值3)
code;;
.....
*)
code;;
esac
举例:
(需求:模拟游戏级别选择,读取键盘录入的数字,如果是数字1,那么输出简单, 如果是数字2,那么输出一般,如果是数字3输出困难,其他输出数据有误)
注意:
1、case 能实现的 if elif 也能实现
2、if elif 能实现的,case不一定能实现了
3、语法上 if elif 更简单
4、但case执行效率高
B、练习
练习1:输入一个序号,判断该用户选择哪款产品,A:笔记本 B:电饭煲 C:小台灯
练习2:请输入对客服的满意度,【0-3】不满意,【4-6】满意,【7-9】非常满意 (了解)
case 也可以执行区间判断,但是区间范围局限于: 0 - 9
区间格式 :[开始数字-结束数字]
9、shell 语法:流程控制循环实现之for
A、格式
语法:
for 变量名 in 列表(v1 v2 v3 ......)
do
code.....#循环体
done
举例:
(需求:遍历(迭代) 1-10之间所有整数)
B、注意
seq 命令优化 for 循环
格式1: seq 参数NUM ---> 默认遍历 [1-NUM] 之间的所有整数
例如:for ele in `seq 15`......
格式2: seq 参数NUM1 参数NUM2 ----> 遍历 [NUM1-NUM2]之间的所有整数
格式3: seq 参数NUM1 参数NUM2 参数NUM3 ----> 遍历 [NUM1-NUM3] 之间的整数,但是每次递增 NUM2 值,不指定 NUM2 每次默认递增1, NUM2 又称之为步进值
C、练习
求1-100之间的和
10、shell 语法:流程控制循环实现之while
A、格式
语法:
while [ boolean表达式 ]
do
code.....
done
举例:
(需求:遍历 1-10之间所有整数)
B、练习
求1-100之间的和
综合练习:输入一个目录,判断目录是否存在,如果不存在则给出提示,如果存在则提示输入要创建的文件名,(前提:要先进入目录)判断创建的文件是否存在,如果不存在,则继续创建,否则提示该文件已经存在
需求:目录操作与文件操作,分别两个函数实现,两个函数中分别实现业务逻辑(分支语句)
11、shell 语法其他:重定向
A、是什么?
可以将命令产生的数据保存到磁盘文件
B、为什么?
一种序列化(持久化)机制,可以持久的保存数据
C、怎么用?
格式1: 命令 1>> 磁盘文件 (将正常结果输出到文件)
例如:ls /usr 1>> hello.txt
格式2: 命令 2>> 磁盘文件 (将错误结果输出到磁盘文件)
例如:lsxz /usr 2>> hello.txt
12、shell 语法其他:数组
A、是什么?
数组也是变量,但是是特殊的变量,一般变量只能存储一个值,而数组可以存储多个值
B、为什么?
因为数组功能更强大
C、怎么用?
数组创建
变量名=(值1 值2 值3 .... )
数组查询
查某个元素: ${数组名[索引]}---->索引从 0 开始
查询所有元素: ${数组名[*]} 或者 ${数组名[@]}
查询元素个数: ${#数组名[*]} 或者 ${#数组名[@]}
数组修改
变量名[索引]=新值
数组删除
unset 数组名(同变量删除)
D、应用
集合循环遍历数组元素