shell 概述
是什么
shell 是一门计算机语言,和 python | Java一样,都可以编写程序
shell字面意思:壳,壳:指操作系统,shell 是保护操作系统的 。
计算机操作系统只能识别 0 和 1组成的机器码,现在我们是通过GUI|CLI 来间接操作操作系统
GUI(图形化界面) | CLI(命令行) 在用户与操作系统之间,相当于桥梁、中介的作用,结构上看,GUI和CLI 保护操作系统
GUI 与 CLI 就是通过 Shell 实现的
分类
第一类:GUI样式的shell(图形化界面)
第二类:CLI样式的shell(命令行)
专指: Linux 下的 shell 编程
怎么用
流程
1)、创建一个文本文档,后缀名是 .sh
文档名要做到见名知意
2)、再在文本文件中录入一个命令
echo "xxxx" 在命令行模式下直接输出数据
规范:第一行固定格式 #!/bin/bash 指定shell解析器在linux位置
作用:指定脚本解析的解释器
3)、执行 .sh 文件(shell 脚本)
方式1: sh(bash) shell文件
方式2: 绝对路径(/xxx/yyy/abc.sh) 或 相对路径(./abc.sh),注意:要修改文件的权限 chmod 777 abc.sh
方式3: source abc.sh(source 相当于 ./)
01.sh
#! /bin/bash
#指定脚本解析的解释器
#echo 是将结果输出到终端上
echo "我是孙腾"
执行shell脚本
练习
练习1:在命令行输出当前所在目录
02.sh
#! /bin/bash
#指定脚本解析的解释器
pwd
执行shell脚本
shell语法变量和运算符
注释
单行注释(常用):一次只注释一行
格式: # 注释文本
变量
<u>变量创建:</u>
格式: 变量名=变量值
注意1:变量名
a)、不能数字开头
b)、变量名不能有空格这种特殊字符
c)、起名做到见名知意
d)、变量名不要使用关键字
注意2:赋值符号"="左右两侧不要有空格
注意3:变量值
a)、变量值可以不使用引号,但是如果有空格,必须使用 "" or ''
b)、"" 和 '' 的区别,变量之间赋值时,如果是 "",那么赋的是变量的值,如果是 '' 只是赋值调用格式
变量查询
格式:"${变量名}" ----- 标准格式 打印时在写代码时候 一定要用双引号这个格式,不然会报莫名错误
注意:其他格式(不建议使用)
${变量名} or $变量名
变量修改:
格式:同增
变量名=变量值
变量删除:
格式:unset 变量名
变量分类
本地(局部)变量
只有当前 shell 可以使用的变量
全局变量(静态变量)(了解)
被多个 shell 共享的变量 (只能在一个终端窗口,不能跨终端,跨终端的env里面没有全局变量)
需求:如何将本地变量转换成全局变量
思想:将本地变量设置为全局变量就是要将本地变量导出到共享空间
格式: export 局部变量
查询全局变量: env
注意1:如果是全局变量,建议变量名所有字母都大写
注意2:全局变量要慎用
内置变量(特殊变量)
需求:编写shell动态获取某个目录下的子级(目录不一定)
实现流程:
1)、shell 调用时,可以传入要操作的目录
格式: sh abc.sh 某个目录
2)、shell 执行时,可以获取调用传入的目录
格式:ls $1 (代表传入的第一个参数)
上述流程其实就是传参以及参数解析的过程,这个参数就可以称之为内部变量
语法总结:
应用场景,程序执行时有些数据是可变的,可以调用脚本时,传入这些可变数据,脚本中解析获取
调用格式: sh xxx.sh 参数1 参数2 参数3 .....
解析格式: $N 获取第 N 个参数
优点: 动态传值,更灵活
$0 获取脚本文件名
$* 获取所有参数
$# 获取参数个数
inner.sh
#! /bin/bash
#ls $1
echo "第1个参数$1"
echo "第2个参数$2"
echo "第3个参数$3"
echo "第9个参数$9"
echo "第10个参数$10"
echo "$0"
echo "$*"
echo "$#"
执行
练习:动态获取值
扩展:读取键盘录入
需求:编写shell动态获取某个目录下的子级(目录不一定,要让调用者指定)
格式: read -p "提示语句:" 变量名 (注意:有空格!!!!)
作用:执行到此时,程序挂起,等待用户录入数据,录入数据后,回车,录入的数据会赋值给变量
优点:动态获取数据,更灵活!!!!!
read.sh
#! /bin/bash
#1、用户录入目录
read -p "请输入一个目录:" myDir
#2、 获取目录子级
ls "${myDir}"
执行
变量特殊赋值(记住)
需求:将某个命令的结果赋值给一个变量
格式:变量名=`命令`
练习
使用shell脚本,输出当前所在的目录
知识点:将命令结果赋值给变量
shell脚本
#! /bin/bash
dir=`pwd`
echo "当前所在目录是:${dir}"
执行shell脚本
计算指定目录下有多少个文件,用shell脚本实现
新知识点:获取某个目录下子级个数
固定格式:ls 目录 | wc -l
参数shell脚本
#! /bin/bash
#动态获取某个目录,然后在获取目录下子级的个数
#方案一:读取脚本调用时传入的参数
#调用 sh test03.sh /root
#执行 ls $1 | wc -l
count=`ls $1 | wc -l`
echo "$1目录下的子级:${count}"
执行
键盘录入shell脚本
#! /bin/bash
#动态获取某个目录,然后在获取目录下子级的个数
#方案一:读取键盘录入的数据
read -p "请你输入一个路径" myDir
# 获取目录子级个数
count=`ls ${myDir} |wc -l`
echo "${myDir}下的子级个数为:${count}"
执行
运算符
算数运算符
格式: $((数学表达式))
运算符: + - * / % == 加减乘除取余
注意: 一般计算机语言中除法运算只取商
比较运算符
返回的是 boolean 值(特殊: 0为true 1为false)
格式: [ 表达式 ] ---- 注意:[] 中有两个空格,两个空格中添加表达式
查看结果: $?
运算符: 不能直接使用 > < >= <= == !=
使用对应的参数 -gt(>) -lt(<) -ge(>=) -le(<=) -eq(==) -ne(!=) **记住**
逻辑运算符
返回 boolean 值(<u>特殊: 0为true 1为false</u>)
格式: [ 表达式 ]
运算符: -a 与 -o或 !非
字符串比较:返回 boolean 值
格式: [ 表达式 ]
运算符: == 判断两个字符串内容是不是一样
!= 判断两个字符串内容是不是不一样
-z 判断单个字符串长度是不是0(判断字符串是不是空)
文件判断:返回 boolean 值
格式: [ 表达式 ]
运算符: -d: 判断是不是文件夹
-f: 判断是不是文件
-e: 判断是不是存在
#常用的文件测试操作符
#常用操作符 #说明
-f 文件,全称file #文件存在且为普通文件则为真,表达式成立
-d 文件,全称directory #文件存在且为目录则为真,表达式成立
-s 文件,全称size #文件存在且大小不为0为真
-e 文件,全称exist #文件存在则为真
-r 文件,全称read #文件存在且为可读则为真,表达式成立
-w 文件,全称write #文件存在且可写为真,表达式成立
-x 文件,全称executable #文件存在且可执行为真
-L 文件,全称link #文件存在且为链接文件为真
f1 -nt f2,英文newer than #文件f1比文件f2新则为真,根据文件修改时间计算
f1 -ot f2,英文older than #文件f1比文件f2旧为真,根据修改时间计算
练习
判断 /home/admin(/root)目录是否为空
思路:现获取目录子级文件个数,判断是否大于 0
#! /bin/bash
#统计指定目录文件个数
read -p "请输入要统计的目录:" str
count=`ls ${str} | wc -l`
echo "${str}目录下共有${count}"
shell语法函数
shell函数常用方式
1. 简单函数(无参)
2. 带参函数(调用函数时带参数)
3. 带参函数(执行脚本时调用参数)
简单函数
说明:自定义函数,调用函数名
格式
定义函数:
函数名(){
语句
...
}
调用函数:
函数名
案例1
需求:定义fun01函数,打印 this is a funcation
#! /bin/bash
#声明一个函数
hello(){
echo "hello function!"
}
#调用函数
hello
执行
带参函数(调用函数时带参数)
说明:调用函数名时带参数
格式
定义函数:
函数名(){
语句$n
...
}
调用函数:
函数名 参数
需求:定义fun01函数,调用的时候传递参数,在函数内部获取参数
#! /bin/bash
# 有参数的函数
getSum(){
echo "两个数字的和为:$(($1+$2))"
echo "第一个参数 $1"
echo "第二个参数 $2"
echo "第三个参数 $3"
echo "第十参数 $10"
echo "文件名:$0"
echo "所有参数:$*"
echo "参数个数:$#"
}
# 调用函数
getSum 4 2 3 4 5 6 7 8 9 a b
结果
带参函数(执行脚本时调用参数)
说明:执行脚本时附带参数
格式
定义函数:
函数名(){
语句$n
...
}
调用函数:
函数名 $n
案例
需求:定义fun01函数,执行脚本时传递参数 lisi,打印:nihao lisi
#!/bin/bash
# 定义函数名
fun01(){
echo "ni hao $1"
}
# 调用函数
fun01 $1
# 执行脚本调用
# bash /root/fun03.sh lisi
带返回值的函数
#! /bin/bash
#带返回值的函数
#1、声明函数
getSum(){
result="$(($1+$2))"
#return "${result}"
echo "函数中的result:${result}"
return 0
}
#2 调用函数
getSum 3 4
#打印返回值
echo "函数返回的结果是:$?"
echo "函数外调用result:${result}"
函数加强练习
使用函数实现,输入任意两个数,打印求和结果
#! /bin/bash
#带返回值的函数
#1、声明函数
getSum(){
result="$(($1+$2))"
#return "${result}"
echo "函数中的result:${result}"
return 0
}
#2 调用函数
getSum 3 4
#打印返回值
echo "函数返回的结果是:$?"
echo "函数外调用result:${result}"
###### 读取键盘录入,录入长方形的长和宽,编写求周长和面积的函数,调用并输出周长和面积的值
#! /bin/bash
#1读取键盘录入的长和宽
read -p "请输入长:" length
read -p "请输入宽:" width
#2求周长函数
getAllLength(){
echo "周长是:$((($1+$2)*2))"
}
#3求面积函数
getArea(){
echo "面积是:$(($1*$2))"
}
#调用周长函数
getAllLength "${length}" "${width}"
#调用面积函数
getArea "${length}" "${width}"
说明:使用函数;提示输入一个目录,在此目录中进行提示要创建的文件名;如果该目录不存在,提示目录不存在;
如果输入的文件已存在,提示该文件已存在,否则创建该文件;
#! /bin/bash
#处理文件的函数
doMyFile(){
#开始处理
#文件存在,给出提示,不存在就创建
if [ -f $1 ]
then
echo "文件已经存在了"
else
echo "文件开始创建"
echo "......"
touch $1
echo "文件创建完毕"
fi
}
#处理目录的函数
doMyDir(){
#获取传入的目录$1
#如果不是目录给出提示,是目录操作文件
if [ -d $1 ]
then
echo "操作文件"
# 让用户录入文件名
read -p "请你录入一个文件:" myFile
# 进入目录
cd $1
#操作文件
doMyFile ${myFile}
else
echo "你输入的不是目录"
fi
}
read -p "请你输入一个目录:" myDir
#操作目录,单独一个函数实现
doMyDir ${myDir}
shell流程控制
说明:如果条件成立,则执行then后面语句,否则执行else后面语句
格式:
if [ 条件 ]
then
语句块
else
语句块
fi
提示:
1. []中括号在shell中表示为表达式,表达式前后必须有空格;如[ 1 -gt 0 ]
2. 条件中有变量或字符串使用""括起来
3. then可以和if语句写在一行,then语句之前需要添加; 如:if [ 条件 ];then
4. 结尾有关键字fi
if流程控制语句
需求: 录入年龄,判断是否成人,如果成人了输出"成年人"
#! /bin/bash
#if单分支实现
#1 让用户录入年龄
read -p "让用户录入你的年龄" age
#2 年龄判断
if [ ${age} -ge 18 ]
then
echo "成年人"
fi
if..else流程控制语句
(需求: 录入年龄,判断是否成人,如果成人了输出"成年人",否则输出未成年)
#! /bin/bash
#if多分支实现
#1 让用户录入年龄
read -p "让用户录入你的年龄" age
#2 年龄判断
if [ ${age} -ge 18 ]
then
echo "成年人"
else
echo "未成年人"
fi
if...elif多分支控制语句
#! /bin/bash
#if多分支实现
#1 让用户录入年龄
read -p "让用户录入你的年龄" age
#2 年龄判断
if [ ${age} -lt 18 ]
then
echo "未成年人"
elif [ ${age} -ge 18 -a ${age} -le 30 ]
then
echo "成年人"
elif [ ${age} -gt 30 -a ${age} -lt 50 ]
then
echo "中年人"
else
echo "老年人"
fi
练习
练习1:判断用户输入的用户名和密码是否为admin 123456,如果是则提示登录成功,否则提示失败
#! /bin/bash
read -p "输入用户名" username
read -p "输入密码" passwd
if [ ${username} == "admin" -a ${passwd} == "123456" ]
then
echo "登陆成功"
else
echo "登陆失败"
fi
练习2:输入数字,判断是否大于0,如果大于0则将该数字-1并输出,否则+1输出
#! /bin/bash
read -p "请你录入一个数字" num
# 如果数字小于0减1 否则加1
if [ ${num} -gt 0 ]
then
num=$((${num} + 1))
echo "${num}"
else
num=$((${num} - 1))
echo "${num}"
fi
练习3:判断用户输入的目录是否存在,如果存在则统计目录下的文件个数,否则提示用户该目录不存在
#! /bin/bash
read -p "请输入一个目录:" myDir
if [ -d ${myDir} ]
then
# 统计文件个数
count=`ls ${myDir} | wc -l`
echo "${myDir}目录下文件个数是:${count}"
else
echo "你输入的目录不存在"
fi
练习3:判断用户输入的内容是否为空,为空则提示,不为空则判断是否为目录,不为目录则判断是否为文件,否则提示错误信息
#! /bin/bash
read -p "请你输入一个文件或目录" myfile
if [ -z ${myfile} ]
then
echo "录入的路径不能为空"
elif [ -f ${myfile} ]
then
echo "录入的路径是文件"
elif [ -d ${myfile} ]
then
echo "录入的路径是文件夹"
else
echo "录入的路径有误"
fi
流程控制分支实现之case
条件符合执行相应的代码块,类似于if..elif..语句
格式:
case 变量 in
值1 )
echo "语句块1"
;;
值2 )
echo "语句块2"
;;
值3 | 值4 )
echo "语句块3"
;;
...
* )
echo "语句块4-默认值"
;;
esac
提示:
1. 星号(*)相当于else,条件都不符合时执行;
2. 双分号(;;)是必须的,执行完相应语句块跳出程序;
3. 竖线(|)用于分割多个值,相当于 值1 or 值2 ;
4. 值可以为字符串、数字、区间值( [0-9] | [a-z] | [A-Z] )、组合词[aA][bB][cC]
4. 结尾必须有esac
需求:模拟游戏级别选择,读取键盘录入的数字,如果是数字1,那么输出简单, 如果是数字2,那么输出一般,如果是数字3输出困难,其他输出数据有误
#! /bin/bash
read -p "请输入一个数据:" num
case "${num}" in
1)
echo "游戏难度简单"
;;
2)
echo "游戏难度一般"
;;
3)
echo "游戏难度困难"
;;
*)
echo "你输入的数据有误"
;;
esac
shell 语法:流程控制循环实现之for
遍历读取列表元素,列表元素遍历完毕,结束语句;
语法格式:
for 值 in 列表
do
执行语句
done
练习
需求:遍历 1-10之间所有整数
#! /bin/bash
# 循环遍历
#for ele in 1 2 3 4 5 6 7 8 9 10
#for ele in `seq 10`
#for ele in `seq 5 10`
for ele in `seq 5 2 10`
do
echo "元素:${ele}"
done
注意
seq 命令优化 for 循环
格式1: seq 参数NUM ---> 默认遍历 [1-NUM] 之间的所有整数
格式2: seq 参数NUM1 参数NUM2 ----> 遍历 [NUM1-NUM2]之间的所有整数
格式3: seq 参数NUM1 参数NUM2 参数NUM3 ----> 遍历 [NUM1-NUM3] 之间的整数,但是每次递增 NUM2 值
不指定 NUM2 每次默认递增1, NUM2 又称之为步进值
求1-100之间的和
#! /bin/bash
#计算1-100之间的整数和
sum=0
for ele in `seq 100`
do
sum=$((${sum}+${ele}))
done
# 输出变量的值
echo "1-100之间的和是:${sum}"
shell 语法:流程控制循环实现之while
while [ boolean表达式 ]
do
code.....
done
(需求:遍历 1-10之间所有整数)
#! /bin/bash
#while循化遍历1-10
num=1
while [ ${num} -le 10 ]
do
echo "${num}"
num=$((${num}+1))
done
求1-100之间的和
#! /bin/bash
#!while循环求1-100的整数和
sum=0
num=1
while [ ${num} -le 100 ]
do
sum=$((${sum}+${num}))
num=$((${num}+1))
done
#循环结束
echo "1-100整数和是:${sum}"
shell 语法其他:重定向
是什么?
可以将命令产生的数据保存到磁盘文件
为什么?
一种序列化(持久化)机制,可以持久的保存数据
怎么用?
格式1: 命令 1>> 磁盘文件 (将正常命令的结果输出到文件)
格式2: 命令 2>> 磁盘文件 (将错误命令的结果输出到磁盘文件)
shell 语法其他:数组
是什么?
数组也是变量,但是是特殊的变量,一般变量只能存储一个值,而数组可以存储多个值
怎么用?
数组创建
变量名=(值1 值2 值3 .... )
数组查询
查某个元素: ${数组名[索引 ]} //索引从 0 开始
查询所有元素: ${数组名[*]} | ${数组名[@]}
查询元素个数: ${#数组名[*]} | ${#数组名[@]}
数组修改
变量名[索引]=新值
数组删除
unset 数组名(同变量删除)