shell 脚本规范
[root@linux-server ~]# vim helloworld.sh ---.sh代表这个文件是个shell脚本,
拓展名后缀,如果省略.sh则不易判断该文件是否为shell脚本
1. #!/usr/bin/env bash ---shebang蛇棒, 解释器, 翻译 2. #
2. #
3. # Author: soso666
4. # Email: soso666@163.com ---这就是注释, 你没看错
5. # Github: https:github.com/soso666
6. # Date: 2019/12/24
7. printf "hello world\n"
功能说明:打印hello world
[root@linux-server ~]# sh helloworld.sh
hello world
[root@linux-server ~]# chmod +x helloworld.sh
[root@linux-server ~]# ./helloworld.sh
[root@linux-server ~]# /root/helloworld.sh
hello world
第一行: “#!/usr/bin/env bash”叫做shebang, shell语法规定shell脚本文件第一行为整个文件的解释器
第二行: 为“#”开头的行为注释行默认不会被程序所读取, 用来说明文件及标定所属人员使用, 也可用来解释程序 第七行: 为格式化打印语句printf, printf可以把后面的“hello world”打印到指定的终端中, \n 为换行符
bash 脚本测试:
1.这将执行该脚本并显示所有变量的值
[root@linux-server ~]# sh -x /root/helloworld.sh
+ printf 'hello world\n'
hello world
2.不执行脚本只是检查语法模式,将返回所有错误语法
[root@linux-server ~]# sh -n /root/helloworld.sh
3.执行脚本前把脚本内容显示在屏幕上
[root@linux-server ~]# sh -v /root/helloworld.sh
#!/usr/bin/env bash
#
# Author: soso666
# Email: soso666@163.com
# Github: https:github.com/soso666
# Date: 2019/12/24
printf "hello world\n"
hello world
变量的类型
变量:bash作为程序设计语言和其它高级语言一样也提供使用和定义变量的功能
分为:预定义变量、环境变量、自定义变量、位置变量
预定义变量
[root@linux-server ~]# $? 最后一次执行的命令的返回状态。如果这个变量的值为 0,则证明上一条命令正确执行;如果这个变量的值为非 0 ,则 证明上一条命令执行错误
[root@linux-server ~]# $$ 当前进程的进程号(PID)
[root@linux-server ~]# $! 后台运行的最后一个进程的进程号(PID)
[root@linux-server ~]# ls
anaconda-ks.cfg a.txt b.txt file.out helloworld.sh #ls命令正确执行
[root@linux-server ~]# echo $?
0
#预定义变量"$?"的值是0,证明上一条命令正确执
[root@linux-server ~]# vim variable.sh
#!/bin/bash
echo "The current process is $$"
[root@linux-server ~]# sh variable.sh
The current process is 1416
#输出当前进程的PID
#这个PID就是variable.sh脚本执行时生成的进程的PID
[root@linux-server ~]# sleep 3000 &
[1] 1418
#符号"&"的意思是把命令放入后台执行
[root@linux-server ~]# echo $!
1418
自定义变量
#定义:变量名称=值
变量名称:只能由字母,数字,下划线组成,不能以数字开头;
#注意:应该让变量名称有意义;
= 赋值符号 前后不能有空格 ;
值: 所有的字符串和数字都可以;
引用变量: $变量名 或 ${变量名}。
示例:
[root@linux-server ~]# a=100
[root@linux-server ~]# echo $a
100
[root@linux-server ~]# echo $aa # 这里输出为空,因为解释器认为$aa是变量
[root@linux-server ~]# echo ${a}a
100a
查看变量: echo $变量名 set(所有变量:包括自定义变量和环境变量)
取消变量: unset 变量名 仅在当前shell中有效
作用范围: 仅在当前shell中生效
环境变量
shell在开始执行时已经定义好的
# env 查看所有环境变量
# set 查看所有变量
环境变量拥有可继承性:export之后就拥有继承性
# export 导出变量(作用范围)
临时生效
[root@linux-server ~]# IPADDR=192.168.1.1
[root@linux-server ~]# echo $IPADDR
192.168.1.1
永久生效
写到4个登陆脚本中 ~/.bashrc ~/profile 更好放在/etc/profile.d/目录下建立独立的环境变量配置文件
[root@linux-server ~]# vim /etc/profile.d/test
IPADDT=192.168.1.1
[root@linux-server ~]# source /etc/profile.d/test #让环境变量生效
[root@linux-server ~]# echo $IPADDT
192.168.1.1
常用环境变量:USER UID HOME HOSTNAME PWD PS1 PATH
PATH:存储所有命令所在的路径
练习1
编写一个shell脚本,用于搜集其执行主机的信息,打印结果如下:
[root@linux-server ~]# mkdir /opt/test/script
[root@linux-server ~]# cd /opt/test/script
[root@linux-server script]# vim test.sh
[root@linux-server script]# chmod +x test.sh
[root@linux-server script]# ./test.sh
现在的时间是: 19年12月24日-21:22
当前的用户是: root
当前的用户标识: 0
当前的主机名称是: linux-server
当前可用网卡IP是: 192.168.246.148/24
##脚本源码如下
#!/usr/bin/bash
# 获取主机基本信息
time=`date +%y年%m月%d日-%H:%M`
ip=`ip a | grep ens33 | awk 'NR==2 {print $2}'`
echo "现在的时间是:" $time
echo "当前的用户是:" $USER
echo "当前的用户标识:" $UID
echo "当前的主机名称是:" $HOSTNAME
echo "当前可用网卡IP是:" $ip
取根分区剩余空间:
[root@linux-server script]# df -Th | awk 'NR==6 {print $5}'
16G
取当前系统剩余内存:
[root@linux-server script]# echo "现在的剩余内存是:"`free -m |awk 'NR==2{print $4}'`
现在的剩余内存是:733
取当前cpu平均负载:
[root@linux-server script]# echo 现在cpu的`uptime |cut -d, -f3-` #-d指定分隔符,-f指定显示区域,3-第三列以后(包括第三列)
现在cpu的 load average: 0.00, 0.01, 0.05
方式二:
[root@linux-server script]# echo 现在cpu的`uptime | awk -F "," '{print $3,$4,$5}'`
现在cpu的 load average: 0.00 0.01 0.05
练习二
编写一个脚本实现显示时间和日期, 列出所有登录系统的用户,并且给出系统的当前时间以及已经运行多长时间.最后脚本还会将这些信息写入一个日志文件.
[root@linux-server script]# vim xinxi.sh
#!/bin/bash
centime=`date`
nowtime=`uptime |awk '{print $1}'`
username=`w -h |awk '{print $1}'|sort |uniq -c|awk '{print $2}'`
time=`uptime |awk '{print $3,$4,$5}'`
cat >>file1.txt <<EOF
echo "时间:$centime"
echo "系统的当前时间是: $nowtime"
echo "系统已运行的时长是: $time"
echo "系统登录的用户有: $username"
EOF
[root@linux-server script]# chmod +x xinxi.sh
[root@linux-server script]# ./xinxi.sh
[root@linux-server script]# cat file1.txt
===========================================================
预定义变量:
$$ 当前进程PID
$? 命令执行后的返回状态.0 为执行正确,非 0 为执行错误
$# 位置参数的数量
$* 所有位置参数的内容
$@ 所有的参数
$! 上一个后台进程的PID (wait命令中使用,后面讲)
拓展:$* 和 $@ 有什么区别
练习. 设计一个shell脚本,要求其统计出占用cpu最高的进程,打印他的pid,在cpu使用率到80%结束进程
置变量
$1 $2 $3 $...
例子:
# /test.sh start #start是第1个位置参数
#/test.sh 2 3 5 hello #2是第1个位置参数,3是第2个位置参数...依次类推
例子:
[root@linux-server ~]# cd /opt/test/script/
[root@linux-server script]# vim weizhi.sh
#!/bin/bash
#...
echo 我的第一个位置参数是:$1
echo 我的第二个位置参数是:$2
echo 我的第三个位置参数是:$3
echo 我的第四个位置参数是:$4
echo 一共有 $# 个位置参数
echo 你输入的参数分别是:$*
[root@linux-server script]# chmod +x weizhi.sh
[root@linux-server script]# ./weizhi.sh 1 3 4 6
我的第一个位置参数是:1
我的第二个位置参数是:3
我的第三个位置参数是:4
我的第四个位置参数是:6
一共有 4 个位置参数
你输入的参数分别是:1 3 4 6
求出第一个参数和第二个参数的和
./5.sh 4 5
9
./5.sh 10 20 30
#!/bin/bash
# 求 $1 $2 的和
x=${1:-0}
y=${2:-0}
echo $(($x+$y))
变量运算
算式运算符: +、-、*、/、()、%取余(取模)
如:(5+3)*2
运算方式:$(()) $[] expr
$(()):例子
# echo $(( 5+2-(3*2)/5 ))
6
# echo $(((3*2)/5))
1
$[]:例子
# echo $[ 5 + 2 - (3*2)/5 ]
6
expr:例子
# expr 5 + 3
8
注意:运算符号两边的空格必须写
乘法运算:
[root@linux-server script]# expr 5 \* 3
15
[root@linux-server script]# expr 5 '*' 3
15
=================================================================
取1到6之间的随机数:
# echo $(($RANDOM % 6 + 1))
5
取1-10之间的随机数:
# echo $(($RANDOM % 10 + 1))
5
[root@linux-server script]# vim sjs.sh
#!/bin/bash
echo $(($RANDOM%50+1))
#这串代码实现了随机生成从1~50之间是数
这串代码特别简单,就是利用RANDOM这个随机数生成器进行取余就能够实现,至于为什么取余时需要+1是因为在取余时如果被 整除那么余数会是0,这样就不在限定范围内了
如下实例是否正确?
#a=1;b=2
#c=$a*$b
#echo $c
1*2
=====================================
#c=$(($a*$b)) #正确写法
# echo $c
浮点运算
bash本身不能做小数计算:需要bc命令转换
# yum install -y bc
# echo "2.6*4" | bc
# echo "2^4" | bc
# echo "scale=2;6/4" | bc
scale: 精度
计算我的信用卡一个月的利息,假设我欠10000块钱
#!/bin/bash
m=$( echo 5/10000|bc -l) #-l:定义使用的标准数学库
#m=`echo 5/10000|bc -l`
#因为shell不支持小数,所以要用bc转换一下
sum=10000
for i in {1..31}
do
sum=$(echo $sum+$sum*$m | bc )
echo $sum
done
echo $sum
简单例子:
#!/bin/bash
sum=1
for i in {1..20} do
done
sum=$(echo $sum+1|bc)
echo $sum
变量引用
转义:\
1.当一个字符被引用时,其特殊含义被禁止
2.把有意义的变的没意义,把没意义的变的有意义
转义案例:
[root@linux-server script]# echo you now $1250
you now 250
[root@linux-server script]# echo you now \$1250
you now $1250
完全引用:'' #强引 硬引
部分引用:"" #弱引 软引
例子:
[root@linux-server script]# num=1
[root@linux-server script]# echo 1901班有$num个女生
1901班有1个女生
[root@linux-server script]# echo "1901班有$num个女生"
1901班有1个女生
[root@linux-server script]# echo '1901班有$num个女生'
1901班有$num个女生
读取用户标准输入:read
read:功能就是读取键盘输入的值,并赋给变量
#read -t 5 var
#read -p "提示信息" var
read后面的变量var可以只有一个,也可以有多个,这时如果输入多个数据,则第一个数据给第一个变量,第二个数据给第二 个变量,如果输入数据个数过多,则最后所有的值都给最后一个变量
案例1:
[root@linux-server script]# vim readtest.sh
#!/bin/bash
# read test
read -p "请输入你的银行卡帐号" num
read -p "请在五秒内输入密码" -t 5 pass
echo "你的密码错误!"
[root@linux-server script]# ./readtest.sh
案例2:
#自定义程序结果的正确或错误
[root@linux-server script]# vim readtest2.sh
#!/bin/bash
read -p "Do you want to continue [Y/N]? " w
case $w in
Y|y)
echo "fine ,continue";;
N|n)
echo "ok,good bye";;
*)
echo "error choice";;
esac
exit 0
[root@linux-server script]# chmod +x readtest2.sh
[root@linux-server script]# ./readtest2.sh
案例3: #-s 选项 能够使read命令中输入的数据不显示在监视器上
[root@linux-server script]# vim readtest3.sh
#!/bin/bash
read -s -p "Enter your password: " pass
echo "your password is $pass"
exit 0
[root@linux-server script]# chmod +x readtest3.sh
[root@linux-server script]# ./readtest3.sh
========================================================
取消屏幕回显
[root@linux-server script]# stty -echo #回车测试
[root@linux-server script]# stty echo #恢复回显
显示变量长度
[root@linux-server script]# a=123
[root@linux-server script]# echo ${#a} #表示$var的长度
3
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
练习:
[root@linux-server script]# cat d.sh
#!/bin/bash
echo 1.配置yum客户端
echo 2.添加A记录
echo 3.一键安装lamp环境
echo 4.一键配置静态IP
read -p "请选择你想使用的功能(1/2/3/4):" num
con_ip(){
echo 这是配置IP地址的小工具
}
case $num in
1):;;
2):;;
3):;;
4)con_ip;;
*):;;
esac
[root@linux-server script]# chmod +x d.sh
[root@linux-server script]# ./d.sh