Linux的shell脚本
1.概述
shell解释器,介于操作系统内核与用户之间,充当了一个“命令解释器”的角色,负责接收用户输入的操作指令(命令)并进行解释,将需要执行的操作传递给内核执行,并输出执行结果。
查看Linux的shell解释器
[root@localhost ~]# cat /etc/shells
/bin/sh
/bin/bash
/usr/bin/sh
/usr/bin/bash
bin -> usr/bin 所以上面2个解释器和下面2个解释器是同一个
sh -> bash 所以上面4个其实本质就是1个
1978年 sh解释器 1987年 bash解释器
2 第一个shell脚本
案例
编写shell脚本
[root@localhost opt]# vim demo.sh
[root@localhost opt]# cat demo.sh
#! /bin/bash 该行是注释,但是告诉操作系统使用/bin/bash解释器来执行下面代码
echo 'hello world!'
执行
sh 文件名
授予执行权限
./文件名
source 文件名
3 变量
变量的命名规则:
1.不能以数字开头
2.严重区分大小写
3.不能是关键字
4.知名见义
5.可以是字母数字下划线
6.驼峰命名法:StudentName
3.1 变量的声明
变量名=值 值的类型有字符串 数字 数组
echo $变量名
注意:1.声明变量时=符号两边不能有空格 如: age=34
2.使用变量时必须前面加$ 如: echo $age
3.2 变量的单引号
[root@localhost opt]# s='我今年$age'
[root@localhost opt]# echo $s
我今年$age
注意:单引号里面的内容不能是引用某个变量
3.2 变量的双引号
[root@localhost opt]# s1="我今年$age"
[root@localhost opt]# echo $s1
我今年34
注意:双引号里的内容可以引用变量
3.3变量的``
[root@localhost opt]# s2="现在的时间是:`date`"
[root@localhost opt]# echo $s2
现在的时间是:2024年 06月 21日 星期五 10:27:16 CST
注意:反单引号``里面是直接作为命令执行
3.4变量的{}
[root@localhost opt]# echo "我今年的age是:${age}aaaa"
我今年的age是:34aaaa
注意:大括号可以告诉操作系统,变量的边界在哪里
3.5 只读变量
[root@localhost opt]# readonly num6=6
[root@localhost opt]# num6=7
-bash: num6: 只读变量
注意:只读变量,无法删除,无法修改,停止shell脚本或者关闭shell窗口,只读变量就会消失
3.6 删除变量
[root@localhost opt]# unset age
[root@localhost opt]# echo $age
注意:变量删除后,就不存在了,输出没有内容
4 输出
echo 内容/$变量
printf 默认不输出换行符
[root@localhost opt]# printf '%d %s %f' $num1 $num2 $num3
1 2 3.000000[root@localhost opt]# printf '%d %s %f\n' $num1 $num2 $num3
1 2 3.000000
[root@localhost opt]#
5 输入
read -p 提示信息 变量名1 变量名2 变量名3
输入值: 值1 值2 值3
[root@localhost opt]# read -p "请输入一个数:" num1 num2
请输入一个数:1 2 3
[root@localhost opt]# echo $num1
1
[root@localhost opt]# echo $num2
2 3
[root@localhost opt]# read -p "请输入一个数:" num1 num2 num3
请输入一个数:1 2
[root@localhost opt]# echo $num1
1
[root@localhost opt]# echo $num2
2
[root@localhost opt]# echo $num3
[root@localhost opt]#
6 位置变量
$1 $2 $3 ... ${10} ${11}
[root@localhost opt]# cat demo.sh
#! /bin/bash
# 这是注释
echo "第一个数为:$1,第二个数为:$2"
[root@localhost opt]# ./demo.sh 1 2
第一个数为:1,第二个数为:2
$1 取第一个值
$2 取第二个值
$3 取第三个值
...
7 预定义变量
$0 文件名
$? 脚本执行结果,执行正常是0,不正常会报错或者其他值
$* 所有传入实参的值
$# 所有传入实参的个数
[root@localhost opt]# cat demo.sh
#! /bin/bash
# 这是注释
echo "$0"
echo "$?"
echo "$*"
echo "$#"
[root@localhost opt]# sh demo.sh 4 5 6 7
demo.sh
0
4 5 6 7
4
8 环境变量
8.1 变量的作用域
- 局部变量:只作用于当前区域
- 全局变量:作用域所有地方
声明局域变量时,正常写就是
声明全局变量时,需要在变量前加export 声明后执行bash
不管是局部变量还是全局变量,执行完shell脚本或关闭shell窗口都会消失,如果想一直存在需要持久化
8.2 持久化变量
想要让变量持久化,不消失,需要把变量写入到环境变量文件中
/etc/profile 针对所有用户
~/.bash_profile 针对当前所有
PATH=$PATH:$HOME/bin
export PATH
export JAVA_HOME=/opt/jdk
export PATH=$PATH:$JAVA_HOME/bin/
export NC_HOME=/opt/netcat-0.7.1
export PATH=$PATH:$NC_HOME/bin
[root@localhost ~]# echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/opt/jdk/bin/:/opt/netcat-0.7.1/bin
[root@localhost ~]# echo $JAVA_HOME
/opt/jdk
9 案例:使用shell编写一个安装jdk脚本
公司上班,10台服务器需要安装jdk,如果一台一台安装浪费大量的时间,使用shell脚本编写好后,直接运行脚本即可完成安装
1.准备工作
理清楚接下来我们要做的事情
- 上传jdk的安装包 优化 使用在线下载 wget....
- 解压安装包,出现一个新的目录
- 去/root/.bash_profile写入环境变量的值 或/etc/profile
- 刷新一下环境变量文件
2.开始脚本
[root@localhost opt]# cat demo.sh
#! /bin/bash
# 定义一些变量,保存jdk安装的相关路径或名字
jdk_package_name='jdk-8u231-linux-x64.tar.gz'
jdk_name='jdk1.8.0_231'
install_path='/opt/'
echo '开始安装jdk···'
tar -zxvf ${jdk_package_name}
echo "配置环境变量···"
cat >> /root/.bash_profile <<EOF
export JAVA_HOME=${install_path}${jdk_name}
export PATH=\$PATH:\$JAVA_HOME/bin/
EOF
echo "刷新环境变量配置文件···"
source /root/.bash_profile
echo "安装完毕!"
10 运算符
10.1 算术运算符
+ 加法
- 减法
* 乘法
/ 除法
% 余数
[root@localhost ~]# a=1
[root@localhost ~]# b=2
[root@localhost ~]# echo $a+$b
1+2
[root@localhost ~]# echo $(($a+$b))
3
[root@localhost ~]# echo $(($a-$b))
-1
[root@localhost ~]# echo $(($a*$b))
2
[root@localhost ~]# echo $(($a/$b))
0
[root@localhost ~]# echo $(($a%$b))
1
echo `expr $a + $b` # +两边必须有空格
echo $(expr $a + $b) # +两边必须有空格
echo $(expr $a \* $b) # 乘法需要是有右斜杠
10.2 比较运算符
>
<
>=
<=
==
!=
10.3 逻辑运算符
&& 与
|| 或
! 非
if [ "var1" -eq 1 ] && [ "var2" -gt 2 ];then
echo "输出内容"
fi
if [ "var1" -eq 1 ] || [ "var2" -gt 2 ];then
echo "输出内容"
fi
if ! [ "var1" -eq 1 ];then
echo "输出内容"
fi
11 数据类型
python:数字型 字符串 列表 元祖 集合 字典
shell:数字 字符串 数组
声明数组
变量名=(值1 值2 值3...)
新增
变量名[索引值]=值
索引值不存在就是新增
[root@localhost ~]# ss=(1 2 3)
[root@localhost ~]# ss[3]=4
删除
unset 变量名[索引值]
[root@localhost ~]# unset ss[0]
查
[root@localhost ~]# echo ${ss[*]} 查询整个数组内容
[root@localhost ~]# echo ${ss[0]} 根据索引值查询某个成员
1
[root@localhost ~]# echo ${ss[1]}
2
[root@localhost ~]# echo ${ss[2]}
3
[root@localhost ~]# echo ${ss[3]}
4
[root@localhost ~]# echo ${#ss[*]} #查询数组内的成员个数
4
改
变量名[索引值]=值
索引值存在就是修改
[root@localhost ~]# ss[0]=99
[root@localhost ~]# echo ${ss[*]}
99 2 3 4
12 分支结构
shell的if语句里,条件可以用(()),[],[[]]
(())主要用于算术比较
例如:if ((10>5));then echo "yes";fi
[]用于字符串比较和文件测试
例如:if [ "$a" = "$b" ];then echo "等于";fi
[[]]bash特有的拓展,提供更强大和灵活的字符串比较和模式匹配功能
例如:if [[ "$a" = "$b" ]];then echo "等于";fi
12.1 单分支
语法:
if 条件
then
执行语句
fi
示例
if (($1>5))
then
echo "这个数大于5"
fi
if [ $1 -eq 5 ]
then
echo "这个数等于5"
fi
12.2 双分支
语法:
if 条件
then
执行语句
else
执行语句
fi
示例
if [ $1 -eq 5 ]
then
echo "这个数等于5"
else
echo "这个数不是5"
fi
12.3 多分支
语法:
if 条件
then
执行语句
elif 条件
then
执行语句
elif 条件
then
执行语句
else
执行语句
fi
if [ $1 -eq 5 ]
then
echo "这个数等于5"
elif [ $1 -gt 5 ]
then
echo "这个数大于5"
else
echo "这个数小于5"
fi
练习:用户输入用户名、密码,如果admin/123456,就输出登录成功,否则输出登录失败
#! /bin/bash
read -p "输入用户名:" username
read -p "输入密码:" passwd
if [ "$username" == "admin" ]
then
if [ "$passwd" == "123456" ]
then
echo "登录成功"
else
echo "登录失败"
fi
else
echo "登录失败"
fi
12.4 case语句
case $变量 in
值1)
执行语句
;;
值2)
执行语句
;;
*)
执行语句
esac
案例:输入一个字符,字符为清华输出清华大学,输入北大,输出北京大学,输入其他字段,输出其他大学
#! /bin/bash
read -p "输入大学名称:" s
case $s in
"清华")
echo "清华大学"
;;
"北大")
echo "北京大学"
;;
*)
echo "其他大学"
esac
13 循环结构
13.1 for循环
语法
for 变量 in 取值列表
do
循环体
done
# 类似于C语言风格
for ((条件初始值;循环结束值;条件的改变))
do
循环体
done
示例:输出1-10数
[root@localhost opt]# cat demo4.sh
#! /bin/bash
for i in {1..10}
do
echo $i
done
示例:计算1-100的和
[root@localhost opt]# cat demo5.sh
#! /bin/bash
sum=0
for i in {1..100}
do
sum=$(($sum+$i))
done
echo $sum
示例:使用shell编写扫描ip主机代码
#! /bin/bash
ip="192.168.2."
for i in {1..255}
do
ipaddr=${ip}${i}
info=`ping -c 1 -W 1 $ipaddr`
if [[ "$info" == *"ttl"* ]]; then
echo "${ipaddr}存在"
else
echo "${ipaddr}不存在==============="
fi
done
13.2 while循环
语法:
while 条件
do
循环体
done
案例:猜数游戏
#! /bin/bash
# num=$[RANDOM%101] 生产一个0-100的整数
num=$[RANDOM%101]
echo $num
read -p "请输入一个数:" input
while [ $num != $input ]
do
if (($num > $input));then
echo "猜小了"
else
echo "猜大了"
fi
read -p "请输入一个数:" input
done
echo "恭喜,猜中了!这个数是:$num"
13.3 循环关键字
- break 终止整个循环
- continue 终止本次循环
- exit 终止整个脚本
#! /bin/bash
num=$RANDOM
echo $num
count=0
read -p "请输入一个数:" input
while [ $num != $input ]
do
if (($num > $input));then
echo "猜小了"
else
echo "猜大了"
fi
((count++))
# count=$(($count+1))
# read -p "请输入一个数:" input
if [ $count == 3 ];then
break
fi
read -p "请输入一个数:" input
done
if [ $count != 3 ];then
echo "恭喜,猜中了!这个数是:$num"
else
echo "失败了,这个数是:$num"
fi
14 函数
语法:
[function] 函数名([形参]){
函数体
[return 值]
}
无参无返
有参无返
无参有返
有参有返
特别注意返回值只能是整数
案例:计算2个数的和
#! /bin/bash
add(){
s=$(($1+$2))
echo $s
}
# 调用函数
add 1 10
#! /bin/bash
add(){
s=$(($1+$2))
return 1
}
# 调用函数
add 1 10
if [ $? -eq 0 ];then
echo "aaa"
else
echo "bbb"
fi