Linux学习-Shell脚本

1.基础概念

机器语言:二进制的0和1的序列,称为机器指令
- 汇编语言:用一些符号来代替机器语言
- 计算机:只能运行二进制指令
- 机器代码文件:由0和1组成的二进制文件
- 编译:将编写好的程序文件通过一个工具(编译器)变成机器代码文件,然后在变成可执行文件。
#编译的特点:效率高
- 解释:将编写好的程序文件中的代码一行一行按顺序通过一个工具(解释器)解释为二进制指令。
#解释的特点:需要把源代码交给使用者
- 编程语言分类:低级语言和高级语言
#高级语言需要经过解释或编译才能转化为计算机能识别的语言
- 编程风格:面向对象和面向过程

静态语言和动态语言
- 静态语言使用变量前需要先定义变量的类型
- 动态语言不用定义变量的类型

强类型和弱类型语言:
- 强类型语言不同类型的数据运算需要经过强制转换
- 弱类型语言:会做隐式转换
shell属于弱类型、动态语言。所以shell变量申明的时候不需要指定变量类型,且不同类型格式进行计算的时候不需要进行强制转换。

2.Shell脚本简介

2.1 什么是shell脚本

1.当命令不在命令行中执行,而是从一个文件中执行时,该文件就是shell脚本。
	shell脚本是普通的文本文件,由流程控制逻辑(if   for  while等)和命令构成。
	shell脚本通常以.sh作为后缀名,但不是必须的。
2.Shell是一种解释型编程语言,不需要编译,执行时也是按行执行。
3.Shell脚本是由解释器解释执行的,常见的解释器有:
   bash dash ash  ksh  sh等
   注意,每种Shell解释器对shell的语法解释并不完全相同(sh和bash解析器对ll的解释就不同),但基本相同。
4. 脚本就是命令的堆砌
5. 脚本可以接受参数,也可以有变量,也可以有流程控制甚至还有函数 

2.2 shell脚本的编制、执行和调试

1.编制:使用文本编辑器vi或者vim
2.执行:执行前必须为脚本文件添加可执行权限
通过添加执行权限,使用bash命令来执行(支持标准输入和输出)

chmod u+x script-file
bash script-file   或者  ./script-file

3.调试

bash -x -n -v script-file
  -n :只读取shell脚本,但不会去执行它,常用来测试脚本语法错误
  -x :进入跟踪模式,显示所执行的每一行命令,常用来调试脚本逻辑
  -v :   在执行script之前,先将script的内容输出到屏幕上

范例:备份并清空日志
备份可变文件存放目录/var 下面的log目录,备份成功后,删除原来文件

vim   backup-1.sh 
#声明使用的解析器
#!/bin/bash
#打包压缩/var/log1文件
tar -czvf  log1.tar.gz  /var/log1
#删除原文件
rm -rf /var/log1     
chmod u+x backup-1.sh
./backup-1.sh
ls    查看本目录 有压缩包
ls   /var/     发现没有log1

该脚本存在很多弊端:
1,不能动态指定备份内容
2,备份后的名称不能随意指定
3,即使备份不成功,也会删除源文件

2.3 shell中的变量

分类:
	自定义变量:
	内置变量:比如PATHHOSTNAMEPS1等
shell变量按生效范围等标准划分:
	普通变量:生效范围只是当前的shell
	环境变量:当前shell以及shell的子进程
	本地变量:函数中
shell中变量的数据类型:
	字符型
	整型、浮点型
shell变量的定义规则:
	区分大小写
	内置变量和保留字不能用
	数字字母下划线(数字不能用作开头)(定义主机名的时候不能使用‘-’)
shell定义变量:
	变量名='值'(等号两边不能有空格) #如果值是字符串最好用引号括起来
#变量临时生效,推出shell后,变量自动删除。
#在Shell中所有的变量默认都是字符串型。也就是说,如果不手工指定变量的类型,那么所有的数值都是不能进行运算的	
shell查看和取消变量:
	查看所有变量: set命令
	取消指定的变量: unset 变量名
shell引用变量:
	$变量名或${变量名}
单引号、双引号、反引号的区别:
	双引号:属于弱引用,里面的变量会替换成对应的值
	单引号:强引用,里面是什么,显示的就是什么
	反引号:命令替换,将反引号内的Linux命令先执行	
shell中的位置变量:
位置变量是bash内置的变量,通过命令行将参数传递给shell脚本的对应参数
	$n       #表示指定位置的参数,比如$1表示的就是shell脚本第一个位置的参数值
	$0       #表示脚本文件的名称
	$*       #表示shell脚本所有的参数
	$#       #表示shell脚本参数的个数
shell脚本安全防范:
	set -e:如果其中一个命令错误,就中断执行
	#有时候会产生误杀,比如执行一条命令返回的状态码是非0
	set -u: 当一个变量没有定义的时候就退出。避免变量不存在出现的问题	

2.4 shell状态码

退出状态码
进程执行完毕以后,会使用$?来保存这个进程的状态码,状态码的取值范围是:0–255

0 #表示执行成功:
1-255 #表示执行失败(因为执行失败有很多可能性,比如语法错误、命令没找到等,所以状态码不一样。)

自定义退出状态码

定义方法: exit [num]
#脚本中一旦遇到exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字
#如果exit后面无数字,终止退出状态取决于exit命令前面命令执行结果
#如果没有exit命令,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码

2.5 shell脚本实现算数运算和逻辑运算

2.5.1 shell脚本的算数运算

shell脚本实现算数运算的方法;

let工具
bc工具(支持小数运算)
expr工具
运算语法

shell的运算语法:

$[算数表达式]
$((算数表达式))

shell算术运算工具的使用:

let工具:
let用于执行一个或多个表达式,变量计算中不需要加 $来表示变量
注意:这种方法需要借助一个变量,将计算后的值赋值给这个变量后进行输出
let a=1+1

bc工具
支持标准输入和输出

echo 1+1 | bc

expr工具

expr 1 + 1 
#数值与运算符号之间需要用空格隔开
#使用expr命令进行乘法运算时,需要将”乘号”转义

运算语法

#$[算数表达式]:
echo $[10+1]
#$((算数表达式))
echo $((1+1))

将变量申明为整数进行计算

#直接将变量声明为”整数”,然后即可直接进行整数运算
declare -i a
a=1+1
echo $a

2.5.2 shell脚本的逻辑运算

运算符

与:&
或:|
非:!
异或:^(脱字符)
短路与&&:前面为真才会执行后面的
短路或 ||:前面为真后面的就不执行了

逻辑运算的结果
逻辑运算的结果是一个布尔值。true或false

true:1(二进制),真
false:0(二进制),假
注意:$?(退出状态码) 为0 ,表示为真,非0则表示为假

2.6 shell的条件测试语句

2.6.1 test命令

用于检查某个条件是否成立,它可以进行数值、字符和文件三个方面的测试
test命令的格式

格式一:test 表达式
格式二: [ 表达式 ] (中括号的两边要有空格作为间隔)
格式三: [[ 表达式 ]] (且支持扩展正则表达式和通配符)

#注意:test和中括号等价
[root@centos8 ~]#help [
[: [ arg... ]
   Evaluate conditional expression.   
   This is a synonym for the "test" builtin, but the last argument must
   be a literal `]', to match the opening `['.

使用test判断文件的权限

-r:是否存在读权限
-x:是否存在执行权限
-w:是否存在写权限
范例:判断一个文件是否存在读权限
test -r aa; echo $?[ -r aa ];echo $?
#若存在,退出状态码为0

文件类型判断

-f:是否为文本文件
-d:是否为目录文件

文件状态判断

-e:文件是否存在
-a:文件是否存在

字符串判断:

-z STRING: 字符串是否为空,没定义或空为真,不空为假
= != > <:连个字符串是否相同、不同等。(这些符号两边要有空格)

数字之间比较

-eq: equal,相等
-ne: not-equal,不等
-gt: greater-than,大于
-lt:less-than,小于

[[ expression ]] 用法

使用通配符的时候: ==!=
会把右侧的认为是通配符

使用正则表达式的时候: =~
会把右侧的认为是正则表达式(扩展正则表达式)

组合条件测试

将两个条件通过并且、或者连接起来
并且: -a
或者: -o

范例:

#如果当前用户非root且该文件不存在就创建这个文件
[ $(whoami) != 'root' -a ! -e /data/dir ] && sudo mkdir /data/dir

2.6.2 read命令

接受键盘的输入,然后把他分配给一个指定的变量。

#若没有指定变量来接受read得到的数据,则默认赋值给内置的变量REPLY
#判断用户输入的是否为YES
#!/bin/bash
read -p 'continute?yes or no:'  ANSWER
[[ $ANSWER  =~  ^([yY][eE][sS])$ ]]  && echo "let's continue" || echo "stop"
#检查主机的网络状态

#!/bin/bash
read -p "please input IP:" IP
[[ "$IP" =~  ^[0-9]{
    
    1,3}\.[0-9]{
    
    1,3}\.[0-9]{
    
    1,3}\.[0-9]{
    
    1,3}$  ]] && echo "$IP is availabe"
ping -c 4 $IP > /dev/null && echo "$IP is Up " || echo "$IP is unreachable"
选项;
-p:指定提示的信息

2.6.3 ()和{}

作用:将多个命令组合在一起,批量执行
格式:( cmd;cmd2…)或{ cmd,cmd2…} --括号左边要有空格

#()和{}批量执行命令的区别:
使用小括号(),里面执行的命令会开启子进程,大括号不会
范例:

#打印X的值
echo hello | read X; echo $X 
#输出的结果为空,因为使用管道的时候会在管道的两边开启子进程。而echo $x是在父进程执行的,子进程的X和父进程的X不是同一个东西

#解决方法: 确保read和echo在一个进程里面进行
echo hell | {
    
      read X; echo $X;}或echo hell | ( read X; echo $X;) #括号左边空格,最末尾的命令要加上分号。
#判断网络的状态
IP="x.x.x.x" #赋值的时候等号两边不能有空格   -c:指定ping的次数
ping -c 4 $IP &> /dev/null  && echo "$IP is up"  || echo "$IP is unreachable";
echo "scripts is finished"
exit
#磁盘空间的判断,判断某块磁盘的使用空间是否达到临界值

WARING=80 #设置一个临界值
SPACE_USE=`df|grep '^/dev/sd'|tr -s ' ' %|cut -d% -f5|sort -nr|head -1`
[ "$SPACE_USED" -ge $WARNING ] && echo "disk used is $SPACE_USED,will be full"

2.6.4 tr命令

作用: 字符转换和删除

选项:
-d:delete
-s:squeeze-repeats
#范例:
转换:tr '123' 'abc' #把132转换成abc -- 一 一对应 1--a 2--b
删除: tr -d '需要删除的内容'
将多个连续的字符压缩或转换成一个指定字符:tr -s '' #把多个连续的空格压缩成一个空格(-s:把相同连续的字符合成一个)

2.6.5 cut命令

作用:提取列

选项:
-d -- 指定分隔符
-f -- 指定提取哪些列

2.6.6 head命令

作用:显示文件的前几行

选项:
-n:指定获取前x行

2.7 shell脚本的流程控制语句

条件选择
条件判断
循环语句

2.7.1 shell的条件选择语句

单分支
双分支
多分支

单分支格式

if 判断条件;then
   执行的操作
fi

双分支格式

if 判断条件;then
   执行的操作
else
   执行的操作
fi

多分支

if 判断条件;then
   执行的操作
elif
   执行的操作
   ......
else
   执行的操作
fi

范例:

#根据命令的退出状态来执行命令
#/bin/bash
read IP
if ping -c 2 $IP &> /dev/null;then
 	echo "$IP is up"
else
	echo "$IP is unreachable"
fi

2.7.2 shell的条件判断语句

case 变量引用 in 
PAT1)
	执行相关操作
	;;
PAT2)
	执行相关操作
	;;
*)
	执行相关操作
	;;
esac

#case语句支持通配符
* 任意长度任意字符
? 任意单个字符
[] 指定范围内的任意单个字符
| 或者,如: a|b

范例:

#判断输入的是yes还是no

#!/bin/bash
read -p 'do you agree(yes|no): ' input

case $input in
y|yes)
	echo 'You input is yes'
	;;
n|no)
	echo 'you input is no'
	;;
*)
	echo 'you input is other'
	;;
esac
#范例2

#!/bin/bash
cat <<EOF
请选择:
1)备份数据库
2)清理日志
3)软件升级
4)软件回滚
5)删库跑路
EOF

read -p '请输入上面的数字进行选择: ' NUM

case $NUM in 
1)
	echo '备份数据库'
	;;
2)
	echo '清理日志'
	;;
3)
	echo '软件升级'
	;;
4)
	echo '软件回滚'
	;;
5)
	echo '删库跑路'
	;;
*)
	echo '以上所有'
	;;
esac

2.7.3 shell的循环语句

for
while
until(不常用)

for循环格式
格式一:

for 变量 in 列表
do
	要做的操作
done
for列表的生成方式
	直接给出列表:1 2 3 4 5 ...
	整数列表:{
    
    start..end}$(seq [start [step]] end)
	通过$()命令替换得到列表

格式二:

for((表达式一;表达式二;表达式三));do
	要做的操作
done

范例:

#使用for循环语句实现1+100
#!/bin/bash
sum=0
for num in `seq 100`;do   #列表1-100的表示方法; {1..100} seq 100
    let sum=$sum+$num  #let
done
echo $sum

#使用seq命令结合bc工具实现:
seq -s+ 100 | bc
-s:指定分隔符,默认分隔符是\n(换行)

#使用tr命令结合bc工具实现:
echo {
    
    1..100}|tr ' ' +|bc
#99乘法表的实现
#!/bin/bash

for i in {
    
    1..9};do
    for j in {
    
    1..9};do
	 if [ $i -ge $j ];then
 	   echo -e "$i * $j = $[i*j]\t\c"  #\c很关键
         fi
    done
    echo
done

#\t:一个制表位,实现对齐的功能 \n:换行符 \c:有了-e参数,我们也可以使用转义字符\c来强制 echo 命令不换行
#将指定目录下文件的所有后缀改名为 bak 后缀
#将/data/下面的所有文件该有bak后缀
#!/bin/bash
DIR="/data/"
cd $DIR  || {
    
     echo "cannot inner this dir";exit; }
for FILE in * ;do #找到/data目录里面的文件列表   *是通配符:表示所有
    PRE=`echo ${
     
     FILE} | grep -Eo ".*\."`  #去掉文件的后缀,如果没有后缀则不会匹配到
    sudo  mv ${FILE}  ${PRE}bakup #给去掉后缀的文件名加上后缀
done
#将目录YYYY-MM-DD/中所有文件,移动到YYYY-MM/DD/下  每天都会生成一个以当天日期为名字目录,里面存放一些文件。

# 1. 创建模拟的实验环境
#!/bin/bash
PDIR=/data/test
for i in {
    
    1..365};do 
 DIR=`date -d "-$i day" +%F` #-d:指定日期 -num:表示当前的前一天
 					#date -d "-1 year" +%F 表示以%F这种格式来显示当前日期的前一天 
 mkdir -pv $PDIR/$DIR #创建指定的文件
 cd $PDIR/$DIR
 for j in {
    
    1..10};do
 touch $RANDOM.log  #随机创建十个文件
 done
done

#将上面的目录移动到YYYY-MM/DD/下
#!/bin/bash
DIR=/data/test #总目录
cd $DIR || {
    
      echo "无法进入 $DIR";exit; }
for subdir in * ; do  #*表示总目录下面的每一个子目录的名称
	YYYY_MM=`echo $subdir | cut -d"-" -f1,2` #取年月
	DD=`echo $subdir | cut -d"-" -f3`  #取日
	[ -d $YYYY_MM/$DD ] || mkdir -p $YYYY_MM/$DD &> /dev/null #判断文件是否存在,不存在就创建一个
	mv $subdir/* $YYYY_MM/$DD #移动文件到新的文件夹下面去
done
rm -rf $/DIR/*-*-* #删除掉旧的文件

-----------------------------------------------------------------------
#date命令的使用
作用:显示和设置系统时间
选项:
-d <字符串> 显示字符串所指的日期与时间,比如:"-1 day" 表示当前日期的前一天,必须要加双引号
-s <字符串> 设置当前的时间和日期
#时间日期格式:
%T:time,显示时分秒,24小时制(hh:mm:ss)
%F:full date,显示年月日,%Y-%m-%d

#范例:
使用date设置年月日:
date -s "20220710"或date -s "YYYY-MM-DD" #时间设置成2022年7月10日0时0分
只设置时间,不改年月日
date -s "hh:mm:ss"
设置全部时间
date -s "YYYY-YY-DD HH:mm:ss"

hwclock命令实现时间校对
-s, --hctosys #以硬件时钟为准,校正系统时钟
-w, --systohc #以系统时钟为准,校正硬件时钟

while循环格式

while 判断条件;do
      所作的操作
done

#循环控制条件;进入循环之前,先做一次判断;每一次循环之后会再次做判断;
#条件为“true”,则执行一次循环;直到条件测试状态为“false”终止循环
#while循环实现1+...100
#!/bin/bash
sum=0
i=1
while [ $i -le 100 ] ;do
  let sum=$sum+$i
  let i+=1
done
echo $sum
#分区利用率的监控
#!/bin/bash
WARING=80
while true ; do  #``:命令运行结果赋值给变量,不加``则把他作为字符串。$()可以直接嵌套
	USE=`df | sed -nr '/^\/dev\/sd/s#.* ([0-9]+)%.*#\1#p'` #使用sed获取分区利用率
	if [ $WARING -lt $USE ];then
	    echo "disk is will be full from `hostname -I`"
	else
	    echo "disk is so big from `hostname -I`" #主机ip地址
	fi
	sleep 2
done

猜你喜欢

转载自blog.csdn.net/weixin_44834205/article/details/128292018