shell 脚本

前言

程序编程的风格

面向过程(Procedure Oriented 简称PO :如C语言):

从名字可以看出它是注重过程的。当解决一个问题的时候,面向过程会把事情拆分成: 一个个函数和数据(用于方法的参数) 。然后按照一定的顺序,执行完这些方法(每个方法看作一个过程),等方法执行完了,事情就搞定了。

面向对象(Object Oriented简称OO :如C++,JAVA等语言):

看名字它是注重对象的。当解决一个问题的时候,面向对象会把事物抽象成对象的概念,就是说这个问题里面有哪些对象,然后给对象赋一些属性和方法,然后让每个对象去执行自己的方法,问题得到解决。

编程 的三种处理逻辑

顺序执行:程序按从上到下顺序执行

选择执行:程序执行过程中,根据条件的不同,进行选择不同分支继续执行

循环执行:程序执行过程中需要重复执行多次某段语句(已知次数    已知条件    死循环)

shell 脚本

1.shell 脚本基础

1.1 shell 的作用

Linux 系统中的 Shell 是一个特殊的应用程序,它介于操作系统内核与用户之间,充当了一个“命令解释器”的角色,负责接收用户输入的操作指令(命令)并进行解释,将需要执行的操作传递给内核执行,并输出执行结果。
常见的 Shell 解释器程序有很多种,使用不同的 Shell 时,其内部指令、命令行提示符等方面会存在一些区别。通过 /etc/shells 文件可以了解当前系统所支持的 Shell 脚本种类。

[root@localhost data]#cat /etc/shells    //查看当前的系统支持的shell
/bin/sh
/bin/bash
/sbin/nologin
/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin
/bin/tcsh
/bin/csh

linux 中常见的 shell

  • bash : 基于gun的框架下发展的shell
  • csh : 类似c语言的shell
  • tcsh : 整合了csh提供了更多功能
  • sh : 已经被bash替换
  • nologin : 让用户无法登录
  • bash (/bin/bash): 是目前大多数Linux 版本采用的默认shell

shell脚本用途

  • 将简单的命令组合完成复杂的工作,自动化执行命令,提高工作效率
  • 减少手工命令的重复输入,一定程度上避免人为错误
  • 将软件或应用的安装及配置实现标准化
  • 用于实现日常性的,重复性的运维工作,如:文件打包压缩备份,监控系统运行状态并实现告警等

1.2 什么是 shell 脚本 以及构成
  • 就是将命令按顺序一一列出,最后自动执行
  • 执行需要权限,也可以直接使用路径    
  • 脚本其实不复杂,通用脚本环境改变后依然可以使用的脚本

其构成为

1.脚本申明(解释器):第一行开头“#!/bin/bash”,表示此行以下的代码语句是通过/bin/bash程序来解释执行。#!/bin/bash  为默认的解释器还有其他类型的解释器,

#!/bin/python           #!/bin/expect

2.注释信息 :以 # 开头的为注释信息。  # 注释

3.可执行语句 :如 echo '你好'  的意思就是 输出 ' ' 中的字符串。

vim fist.sh

#!/bin/bash
echo 'hello word'
echo '当前所在位置:'
pwd

shell 脚本_第1张图片

搭建本地yum仓库

[root@localhost data]#cat yum.sh
#!/bin/bash
mkdir /gua
mount /dev/sr0 /gua
cd /etc/yum.repos.d
mkdir bak
mv *.repo bak
echo "[local]
name=local
baseurl=file:///gua
gpgcheck=0" >/etc/yum.repos.d/local.repo
yum clean all
yum makecache

[root@localhost data]#bash yum.sh 
mount: /dev/sr0 写保护,将以只读方式挂载
已加载插件:fastestmirror, langpacks
正在清理软件源: local
Cleaning up everything
Maybe you want: rm -rf /var/cache/yum, to also free up space taken by orphaned data from disabled or removed repos
Cleaning up list of fastest mirrors
已加载插件:fastestmirror, langpacks
local                                                                                   | 3.6 kB  00:00:00     
(1/4): local/group_gz                                                                   | 156 kB  00:00:00     
(2/4): local/filelists_db                                                               | 3.1 MB  00:00:00     
(3/4): local/primary_db                                                                 | 3.1 MB  00:00:00     
(4/4): local/other_db                                                                   | 1.2 MB  00:00:00     
Determining fastest mirrors
元数据缓存已建立

1.3 脚本执行逻辑及执行方式

脚本执行逻辑

  1. 顺序执行:程序按从上到下顺序执行

  2. 选择执行:程序执行过程中,根据条件的不同,进行选择不同分支继续执行

  3. 循环执行:程序执行过程中需要重复执行多次某段语句

脚本执行方式

1.使用路径执行             (需要执行权限)

[root@localhost ~]# chmod +x /root/host.sh    加权限
指定相对路径./host.sh
指定绝对路径/root/host.sh

2.直接使用解释器         (不需要执行权限)

[root@localhost data]#bash fist.sh 
hello
[root@localhost data]#ll fist.sh 
-rw-r--r--. 1 root root 25 1月  22 15:57 fist.sh

3.source 和 . 执行        (不需要执行权限)

[root@localhost data]#source fist.sh 
hello
[root@localhost data]#
[root@localhost data]#. fist.sh 
hello

使用 前两种 方式执行脚本会开启 子 bash 环境去执行脚本。

使用 source 和 . 会影响当前的 bash 环境。                      不推荐使用

例如:

[root@localhost data]#cat q.sh 
#!/bin/bash
echo 'hello'
cd /mnt
touch 123

# 使用 bash 执行这个脚本

[root@localhost data]#bash q.sh 
hello
[root@localhost data]#ll /mnt
总用量 0
-rw-r--r--. 1 root root 0 1月  22 16:02 123

可以看到 bash 对当前终端无影响 ,我们试试用 source

[root@localhost data]#source q.sh 
hello
[root@localhost mnt]#
[root@localhost mnt]#ls
123

可以看到 脚本里的 cd /mnt 命令在当前终端执行了 ,影响了当前终端。

1.4 脚本命令错误调试

1、命令错误

        命令出错不会影响接下来的命令继续

[root@localhost mnt]#vim 1.sh
echo "hello world"
hstname
ifconfig ens33|head -n2

[root@localhost mnt]#bash 1.sh 
hello world
1.sh:行2: hstname: 未找到命令
ens33: flags=4163  mtu 1500
        inet 192.168.80.7  netmask 255.255.255.0  broadcast 192.168.80.255

2、语法错误

        会影响接下来的命令继续

[root@localhost mnt]#vim 1.sh
echo "hello world"
hstname

cat > /data/kgc.tx <

3、逻辑错误

        只能自己去筛查

bash -n 脚本名称 (不在当前目录下加绝对路径)     检查语法错误
bash -x 脚本名称 (不在当前目录下加绝对路径)     逻辑错误


bash  -n  脚本名称
检测语法错误

bash  -x  脚本名称
将脚本的所有语句执行一遍。

总结:脚本错误常见的有三种区别

1. 语法错误,会导致后续的命令不继续执行,可以用bash -n 检查错误,提示的出错行数不一定是准确的。

2. 命令错误,默认后续的命令还会继续执行,用bash -n 无法检查出来 ,可以使用 bash -x 进行观察。

3. 逻辑错误:只能使用 bash -x 进行观察。

1.5 停止执行与不执行

set  -e    出错立即停止

[root@localhost mnt]#vim 1.sh
set -e                                   #在  脚本的前面输入  set -e   一旦出错立即停止
echo "hello world"
hstname
ifconfig ens33|head -n2

[root@localhost data]#bash 1.sh 
hello world
1.sh:行3: hstname: 未找到命令

set  -u    变量不存在不执行

set -u    变量不存在不让执行
DIR=/opt

rm -rf  $DIr/data     #!!!!!!!注意 /data 前面变量不在无异于 rm -rf /data

2. 重定向与管道符

2.1 重定向
类型 设备文件 文件描述编号 默认设备
标准输入 /dev/stdin 0 键盘
标准输出 /dev/stdout 1 显示器
标准错误输出 /dev/stderr 2 显示器

交互式硬件设备

  • 标准输入:从该设备接收用户输入的数据
  • 标准输出:通过该设备向用户输出数据
  • 标准错误:通过该设备报告执行出错信息

重定向的意思就是 ,不输出到默认设备上,输出到你指定的位置

1     代表 正确输入
2     代表 错误输入
&    代表混合

类型 操作符 用途
重定向输入 < 从指定的文件读取数据,而不是从键盘输入
重定向输出 1> 将输出结果保存到指定的文件(覆盖原有内容)
追加输出 >> 将输出结果追加到指定的文件尾部
标准错误输出 2> 将错误信息保存到指定的文件(覆盖原有内容)
追加错误输出 2>> 标准错误输出结果追加到指定的文件尾部
混合输出 &>无论对错都可以重定向 将标准输出、标准错误的内容保存到同一个文件中
[root@localhost ~]#ls >/dev/pts/1
#打开两个终端,ls命令显示到终端1上 标准重定向省略了1>

[root@localhost ~]#xxx >/dev/pts/1
##错误重定向
bash: xxx: 未找到命令...
[root@localhost ~]#xxx 2>/dev/pts/1
##会在另一边显示

[root@localhost ~]#rm xxx.txt >/dev/pts/1
#提示信息也是错误
rm: 无法删除"xxx.txt": 没有那个文件或目录
[root@localhost ~]#rm xxx.txt 2>/dev/pts/1

[root@localhost ~]#ls /data /error >all.log 2>&1
#既有错误也有正确 &符号表示分隔符
[root@localhost ~]#cat all.log 
ls: 无法访问/error: 没有那个文件或目录
/data:
apr-1.6.2.tar.gz
apr-util-1.6.0.tar.gz
httpd-2.4.29.tar.bz2

[root@localhost ~]#ls /data /error >all.log 2>1
#如果没有&会生成一个 1文件 将错误导入

[root@localhost ~]#cat bc.log 
2*6
[root@localhost ~]#bc  passwd.txt     //单个覆盖,多个不覆盖追加
[root@test1 ~]# cat passwd.txt 
123123
[root@test1 ~]# ls > passwd.txt            //将命令结果放进文本中
[root@test1 ~]# cat passwd.txt 
bin
cpu
disk.sh
first.sh
f.sh
gs.sh
g.txt
passwd.txt
sdb.sh
source
test.sh

[root@test1 ~]# cat passwd.txt 
123123
[root@test1 ~]# echo "123123" >> passwd.txt 
[root@test1 ~]# cat passwd.txt 
123123
123123
[root@test1 ~]# id wa 2>error.log     //将错误信息写入error.log
[root@test1 ~]# cat error.log 
id: wa: no such user
[root@test1 ~]# id wa >error.log      //不加2 无法写入
id: wa: no such user
[root@test1 ~]# cat passwd.txt error.log &>new   //混合输入无论对错
[root@test1 ~]# cat new 
bin
cpu
disk.sh
first.sh
f.sh
gs.sh
g.txt
passwd.txt
sdb.sh
source
test.sh
cat: error.log: 没有那个文件或目录

有几种方法 将错误结果和正确结果一起导给 一个文件中?

[root@centos7 ~]#ls /data /xxx 1> /data/all.log 2>&1
[root@centos7 ~]#ls /data /xxx 2> /data/all.log 1>&2 
[root@centos7 ~]#ls /data /xxx &> /data/all.log  
[root@centos7 ~]#ls /data /xxx >& /data/all.log
#以上方法可以把 错误输出和正确输出 一起导给 /data/all.log 文件中


[root@centos7 ~]#ls /data /xxx   2>&1 1> /data/all.log
[root@centos7 ~]#ls /data /xxx   1>&2 2> /data/all.log
#此命令不行
2.2 多行重定向

        Here Document 概述
使用 I/O 重定向的方式将命令列表提供给交互式程序或命令,比如 ftp、cat 或 read 命令。
HereDocument是标准输入的一种替代品,
可以帮助脚本开发人员不必使用临时文件来构建输入信息,
而是直接就地生产出一个文件并用作命令的标准输入。

[root@localhost data]#wc -l < 123
> 123
> EOF
2





[root@localhost ~]#cat > 1.txt
#需要回车才会写入
hhh
111
123


[root@localhost ~]#cat >file.txt <11.txt < 123
> 345
> 567
> EOF



#####修改密码
[root@localhost data]#passwd zhangsan < 123123
> 123123     #需要输入两次密码
> EOF
更改用户 zhangsan 的密码 。
新的 密码:无效的密码: 密码少于 8 个字符
重新输入新的 密码:passwd:所有的身份验证令牌已经成功更新。



###变量赋值
[root@localhost ~]# read a < 10
> EOF
[root@localhost ~]# echo $a
10
[root@localhost data]#read i < fcks
> EOF
[root@localhost data]#echo $i
fcks



支持变量替换
在写入文件时会先将变量替换成实际值,再结合 cat 命令完成写入
#!/bin/bash
file="EOF1.txt"
i="school"
cat > $file <
 2.3 管道符

将左侧的命令输出结果,作为右侧命令的输入(处理对象)可以 叠加使用

[root@localhost opt]# ls /opt |wc
      2       2      12

修改密码
[root@localhost opt]# echo "123123" |passwd --stdin zhangsan
更改用户 zhangsan 的密码 。
passwd:所有的身份验证令牌已经成功更新

[root@test1 ~]# grep "/bin/bash$" /etc/passwd
root:x:0:0:root:/root:/bin/bash
zhangsan:x:1000:1000:zhangsan:/home/zhangsan:/bin/bash
lisi:x:1001:1001::/home/lisi:/bin/bash
[root@test1 ~]# grep "/bin/bash$" /etc/passwd | awk -F: '{print $1,$7}'
root /bin/bash
zhangsan /bin/bash
lisi /bin/bash

3. 变量

变量来源于数学,是计算机语言中能储存计算结果或能表示值的抽象概念。

保存将来会变化的数据,即使数据变化,直接调用变量即可,各种 Shell 环境中都使用到了“变量”的概念。Shell 变量用来存放系统和用户需要使用的特定参数(值),而且这些参数可以根据用户的设定或系统环境的变化而相应变化。通过使用变量,Shell 程序能够提供更加灵活的功能,适应性更强。

3.1 变量基础

常见 Shell 变量的类型包括:

自定义变量 :由用户自己定义,修改和使用
预定义变量 :Bash中内置的一类变量    shell           不能修改    规定好的变量 放在那里让你使用

环境变量:由系统维护,用于设置工作环境      当前目录  当前主机名啊   $PATH     
只读变量:只可以读取不可以更改                 只能不能修改    常量   
位置变量:通过命令行给脚本传递参数

系统内置变量:PATH,UID,HOSTNAME,USER 

3.1.1 变量命名要求
  • 区分大小写
  • 不能使程序中的保留字和内置变量:如:if, for,hostname   命令  a=   
  • 只能使用数字、字母及下划线,且不能以数字开头,注意:不支持短横线 “ - ”,和主机名相反 
  • 不要使用内置的变量,使用英文尽量使用词义通俗易懂,PATH              
  • 大驼峰 StudentFirstName 
  • 小驼峰 studentFirstName 
  • 下划线 student_name

可以使用 unset 删除变量。

name='value' 
变量名=变量值

变量名开头不能有数字
[root@localhost ~]#1a=1x
bash: 1a=1x: 未找到命令...


直接字串:name='root'
变量引用:name="$USER"
命令引用:name=`COMMAND` 或者 name=$(COMMAND)
注意:变量赋值是临时生效,当退出终端后,变量会自动删除,无法持久保存,脚本中的变量会随着脚本结束,也会自动删除


重要符号

双引号  " "    弱引用    可以识别变量
单引号  ' '   强引用    不能识别变量

弱引用和强引用
"$name "  弱引用,其中的变量引用会被替换为变量值
'$name'   强引用,其中的变量引用不会被替换为变量值,而保持原字符串



{       }              定义变量名的范围
`  `反撇                调用命令执行的结果    与$() 功能一样

定义变量
变量名=变量值   a=10

调用变量
echo $a




[root@localhost opt]# producet=qwq
[root@localhost opt]# echo $producet 
qwq

[root@localhost opt]# version=y
[root@localhost opt]# echo $producet$version
qwqy

[root@localhost opt]# echo "$producet$version"
qwqy

[root@localhost opt]# echo '$producet$version'
$producet$version

[root@localhost opt]# echo $producet6.0
.0

[root@localhost opt]# echo ${producet}zxz
qwqzxz
[root@localhost opt]# echo ${producet}$version
qwqy



[root@node2 ~]#name=xiaoming
[root@node2 ~]#title=cto
[root@node2 ~]#echo $title_$name
xiaoming




[root@localhost ~]#echo ls
ls
[root@localhost ~]#echo $(ls)
abc.txt anaconda-ks.cfg initial-setup-ks.cfg 公共 模板 视频 图片 文档 下载 音乐 桌面
[root@localhost ~]#echo `ls`
abc.txt anaconda-ks.cfg initial-setup-ks.cfg 公共 模板 视频 图片 文档 下载 音乐 桌面

1.赋值时使用双引号(" ")可以直接调用变量

2.赋值时使用单引号(' ')变量$只会被认为是字符$ 不会调用变量

3.赋值时使用(``反撇在tab上面)命令替换,提取命令执行后的输出结 果 和$( ) 用法相同

4.{}可以分隔变量值

变量追加值

追加变量

变量名+=追加内容

[root@localhost ~]#name=wang
[root@localhost ~]#
[root@localhost ~]#echo $name
wang
[root@localhost ~]#name+=yi
[root@localhost ~]#
[root@localhost ~]#echo $name
wangyi



修改环境变量
PATH+=:/data

[root@localhost ~]#vim /etc/profile
PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/data"
3.1.2 read -p

交互式输入变量值 ,然后使用变量

方法1:

[root@localhost ~]#read  -p  "请输入ip地址:"    ip      #这个ip是变量名
请输入ip地址:192.168.80.7                               #手动输入192.168.80.7
[root@localhost ~]#ping -c2 $ip
PING 192.168.80.7 (192.168.80.7) 56(84) bytes of data.
64 bytes from 192.168.80.7: icmp_seq=1 ttl=64 time=0.040 ms
64 bytes from 192.168.80.7: icmp_seq=2 ttl=64 time=0.034 ms

--- 192.168.80.7 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1000ms
rtt min/avg/max/mdev = 0.034/0.037/0.040/0.003 ms



[root@localhost data]#vim xxi.sh
#!/bin/bash
read -p "输入你的班级名称:" ban1
echo "您的班级是$ban1"

sleep 2

read -p "输入你的学号:" xue1
echo "您的学号是$xue1"


如何使用脚本计算小数相加?
[root@localhost data]#vim xiaoshu.sh
#!/bin/bash
read -p "输入第一个数字:" a1
read -p "输入第二个数字:" a2
echo "计算结果为:`echo "$a1+$a2"|bc`"

[root@localhost data]#bash xiaoshu.sh 
输入第一个数字:1.3
输入第二个数字:1.15
计算结果为:2.45

方法2:

[root@localhost opt]# vim 1.sh
#!/bin/bash
echo -n "请输入你的信息"
read info
echo $info


[root@localhost data]#bash 1.sh 
请输入你的信息12121
12121

#12121是输入完后回车的结果


[root@test1 ~]# vim xinxi.sh
#!/bin/bash
echo "请输入你的班级"
read num1
echo "您输入的班级是$num1" 

sleep 2

echo "请输入你的学号"
read num2
echo "您输入的学号是$num2"

运用:

[root@localhost opt]# vim 1.sh

#!/bin/bash
read -p "是否覆盖当前文件(yes/no)" ACK
[ $ACK = "yes" ] && echo "覆盖" || echo "不覆盖"


[root@localhost data]#bash 1.sh 
是否覆盖当前文件(yes/no)no
不覆盖
[root@localhost data]#bash 1.sh 
是否覆盖当前文件(yes/no)yes
覆盖
3.1.3 变量的作用范围

        默认情况下,新定义的变量只在当前的shell环境中有效,因此称为局部变量,当进入子程序或新的shell环境中,局部变量将无法再起作用。

        可以通过内部命令 export 将指定的变量为全局变量,使用户定义的变量在所子shell环境中可以继续使用

方法:

1. 格式1:export   变量名
2. 格式2:export   变量名=变量值

可以使用 pstree 查看 shell 的环境

输入bash 进入子shell

ctrl+D组合 或 输入 exit 退出子shell

举例:

[root@localhost opt]# abc=123
[root@localhost opt]# echo $abc 
123
[root@localhost opt]# bash
[root@localhost opt]# echo $abc
为空
[root@localhost opt]# exit
exit
[root@localhost opt]# echo $abc
123


[root@localhost opt]# export abc
#export  变量名      定义全局变量
[root@localhost opt]# bash          #进入子shell
[root@localhost opt]# echo $abc
123
3.1.4 整数的运算

运算符:

加法   +

减法   -

乘法   \*

除法   /

取余(取模)  %

这些只能进行整数的运算

(1)let  变量名=算数表达式

[root@localhost data]#let sum=1+2
[root@localhost data]#
[root@localhost data]#echo $sum
3


[root@localhost ~]#a=1
[root@localhost ~]#b=2
[root@localhost ~]#let z=a+b
[root@localhost ~]#echo $z
3


[root@localhost ~]#let z=$[a-b]
[root@localhost ~]#echo $z
-1


[root@localhost ~]#let z=$((a-b))
[root@localhost ~]#echo $z
-1

(2)((变量名=算术表达式))

[root@localhost data]#((sum=1+10))
[root@localhost data]#echo $sum
11


[root@localhost ~]#sum=6
[root@localhost ~]#yu=4
[root@localhost ~]#echo $((sum+yu))
10


[root@localhost opt]# echo $((10 + 2))
12

(3)变量名=$[算术表达式]

[root@localhost data]#sum=$[1+123]
[root@localhost data]#echo $sum
124


[root@localhost ~]#sum=6
[root@localhost ~]#yu=4
[root@localhost ~]#echo $[sum+yu]
10


[root@localhost ~]#echo $[10+2]
12

(4)expr

[root@localhost ~]#a=1
[root@localhost ~]#b=2
[root@localhost ~]#expr $a + $b
#加减乘除前后有空格
3


[root@localhost opt]# expr 1+2
1+2
[root@localhost opt]# expr 1 + 2     注意空格
3
[root@localhost opt]# a=20
[root@localhost opt]# b=30
[root@localhost opt]# expr $a + $b
50
[root@localhost opt]# expr $a - $b
-10
[root@localhost opt]# expr $a \* $b
600
[root@localhost opt]# expr $b / $a
1
[root@localhost opt]# expr $b % $a
10
[root@localhost opt]# expr 1 + 2 \* 3 
7
[root@localhost opt]# expr 2 \* $[ 3 + 1 ] 
8

(5) 变量名=$(expr $arg1 $arg2 $arg3 ...)

[root@localhost ~]#sum=10
[root@localhost ~]#yu=123

[root@localhost ~]#var=$(expr $sum + $yu)     #加减乘除前后有空格
[root@localhost ~]#echo $var
133


(6) var= `expr arg1 arg2 arg3 ...`

[root@localhost ~]#sum=10
[root@localhost ~]#yu=123
[root@localhost ~]#cho=`expr $sum + $yu`    #加减乘除前后有空格
[root@localhost ~]#echo $cho
133


(7) echo '算术表达式' | bc                  #能够计算小数

[root@localhost ~]#echo '1.2+12.4'|bc
13.6

[root@localhost opt]# echo "1.2*1.3" |bc
1.5
#计算时有几位,结果就有几位  本来结果应为 1.56


[root@localhost ~]#echo "1.11*2.2"|bc
2.44
[root@localhost ~]#echo "scale=3;1.11*2.2"|bc
2.442

scale=3 :显示四位数字(小数点后三位)


拓展:

let 支持加加减减 使用较多
[root@localhost ~]#i=1
[root@localhost ~]#let i++
[root@localhost ~]#echo $i
2
[root@localhost ~]#i=1;let i++;echo $i
2
[root@localhost ~]#i=1;let ++i;echo $i
2
[root@localhost ~]#i=100;let j=i++;echo $i $j
101 100
[root@localhost ~]#i=100;let j=++i;echo $i $j
101 101
#i++ 是先赋值给ii后再加     ++i是加后再赋值

i++  是先赋值再加
++i  是加后再赋值




i=$(expr 12  \ *  5 )

i=$((12 * 5))

i=$[12 * 5]

let i=12*5

i++ 相当于 i=$[ $i+1 ]

i-- 相当于 i=$[ $i - 1 ]

i+=2 相当于 i=$[ $i+2 ]

随机数生成器变量:

$RANDOM   取值范围:0-32767



echo  $[RANDOM%34]

#这段的意思是输出 0-33 的随机数。

echo  $[RANDOM%34 +1]

#加1 后的意思是随机输出 1-34 的随机数。


[root@localhost ~]#echo -e "\E[1;30mhello\E[0m"
#颜色
[root@localhost ~]# echo -e "\E[1;$[RANDOM%7+31]mhello\E[0m"
#随机颜色

[root@localhost ~]#echo $(expr $RANDOM % 33 + 1)
#注意运算符附近都要有空格

如何使用脚本提取系统信息?

#!/bin/bash
name=`hostname`
ip=`ifconfig ens33|grep netmask|tr -s " "|cut -d " " -f3`
cpu=`lscpu|grep 型号名称|tr -s " "|cut -d " " -f2-7`
k=`uname -r`
os=`cat /etc/redhat-release`
disk=`lsblk|grep disk|tr -s " "|cut -d " " -f4`
mem=`free -h|grep -i mem|tr -s " "|cut -d " " -f2`

echo -e "\E[1;32m------------------------system info-----------------------\E[0m"
echo "主机名:       $name"
echo "IP地址:       $ip"
echo "cpu型号        $cpu"
echo "内核版本       $k"
echo "系统类型       $os"
echo "磁盘大小       $disk"
echo "内存大小       $mem"
echo -e "\E[1;32m------------------------    end     -----------------------\E[0m"
3.2 环境变量

1. 由系统提前创建,用来设置用户的工作环境
2. 可以使用env查看环境变量
3. 需要记住的常用环境变量

[root@localhost ftp]#env
#可以看到所有环境变量

$USER 表示用户名称

$HOME 表示用户的宿主目录

$LANG 表示语言和字符集

$PWD 表示当前所在工作目录

$PATH 表示可执行用户程序的默认路径

环境变量:

  • 可以使子进程(包括孙子进程)继承父进程的变量,但是无法让父进程使用子进程的变量
  • 一旦子进程修改从父进程继承的变量,将会新的值传递给孙子进程
  • 一般只在系统配置文件中使用,在脚本中较少使用
export命令用于设置或显示环境变量。
#声明并赋值   这样在其它bash 环境里也可以使用此变量
export name=VALUE
declare -x name=VALUE
#或者分两步实现
name=VALUE
export name


系统可以通过$PATH 来执行文件 PATH=$PATH:/root/
[root@localhost opt]# PATH=$PATH:/root/
[root@localhost opt]# echo $PATH 
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root/


#如果不加 $PATH 的后果
[root@localhost data]#PATH=:/root
[root@localhost data]#echo $PATH
:/root
#可以看到只有/root了


或者可以这样
[root@localhost data]#PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root
[root@localhost data]#echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/root/bin:/root
3.2.1 环境变量的全局配置文件

配置文件位置在

/etc/profile  如果修改此文件会作用于所有用户

~/.bash_profile  用户独立的配置文件,修改这个文件只作用于当前用户

可以用来长期变更或设置环境变量

[root@localhost ~]# vim /etc/profile
.....................省略到行末添加
export PATH=$PATH:/root

保存并退出后更改内容还没有生效 ,使用 source 或 .  立即生效

[root@localhost opt]# source /etc/profile
[root@localhost opt]# . /etc/profile


修改系统默认的命令数
[root@localhost opt]# echo $HISTSIZE   #历史命令的最大条数
1000
[root@localhost opt]# vim /root/.bash_profile
export HISTSIZE=200
[root@localhost opt]# source /root/.bash_profile 
[root@localhost opt]# echo $HISTSIZE 
200
3.2.2 只读变量  readonly

变量值不允许修改(重新赋值)的情况

无法使用 unset删除

最快方法重启

[root@localhost opt]# name=kjk
[root@localhost opt]# readonly name
[root@localhost opt]# echo $name 
kjk
[root@localhost opt]# unset name
bash: unset: name: 无法反设定: 只读 variable
#只有退出进程
[root@localhost opt]# echo $name 
kjk
[root@localhost opt]# name=jnj
bash: name: 只读变量
3.2.3 位置变量

位置变量也称为位置参数,使用$1、$2、$3、…、$9 表示

[root@test1 ~]# vim 1.sh      
#!/bin/bash
echo "$1"            位置1
echo "$2"            位置2
echo "${10}"         位置10
echo "$10"           位置1和0
echo "$*"            将所有项当成一个值
echo "$@"            所有项
echo "$0"            脚本自身
echo "$#"            后面参数的个数

shell 脚本_第2张图片

范例:

[root@test1 ~]# vim weizhi.sh
#!/bin/bash
sum=$[$1+$2]
echo "$1+$2=$sum"


[root@test1 ~]# bash weizhi.sh 12 34 56
12+34=46
[root@test1 ~]#cat qiuhe.sh
#!/bin/bash
i=$1
m=$2
sum=0
let sum=$[i+m]
echo $sum
3.2.4 预定义(状态)变量
  • $*  :表示所有位置参数的内容看成一个整体返回 返回所有

  • $@  :表示所有位置参数的内容分割成n份,每份作为一个独立的个体返回 返回所有

  • $?  :表示前一条命令执行后的返回状态,返回值为 0 表示执行正确,返回任何非 0值均表示执行出现异常

  • $#  :表示命令行中位置参数的总个数

  • $0  :表示当前执行的脚本或程序的名称 当前脚本的名字

  • $$  :当前bash的进程id

  • $!  : 后台任务最后一个id

shell 脚本_第3张图片

注意:$@      $* 只在被双引号包起来的时候才会有差异

[root@test1 ~]# vim 1.sh      
#!/bin/bash
echo "$*"            将所有项当成一个值
echo "$@"            独立个体
echo "$#"            后面参数的个数


注意:$@ $* 只在被双引号包起来的时候才会有差异
将脚本1的结果 $@ 交给3 输出$1
将脚本2的结果 $* 交给3 输出$1
[root@localhost opt]# bash 1.sh  a  b c
#!/bin/bash
echo "@结果"
./3.sh "$@" 

[root@localhost opt]# cat 2.sh 1 2 3
#!/bin/bash
echo "*结果"
./3.sh "$*" 


[root@localhost opt]# cat 3.sh 
#!/bin/bash
echo "$1"

shell 脚本_第4张图片

$* 与  $@ 的区别

[root@localhost data]#vim dayin.sh

#!/bin/bash
echo "打印出\$*"
for var in "$*"
do
echo "$var"
done


echo "打印出\$@"
for var in "$@"
do
echo "$var"
done



[root@localhost data]#bash dayin.sh 12 123 1234
打印出$*
12 123 1234
打印出$@
12
123
1234

由此可以看出 ,$* 是把 12 123 1234 当做一个整体来输出的
$@   是把 12 123 1234 当作几个个体来输出的
4. 条件语句
4.1 测试
格式1:test  条件表达式
格式2:[  条件表达式  ]
注意[]空格,否则会失败
测试 是否成功使用 $?  返回值
[ 操作符 文件或目录 ]
help test

操作符:
-d:测试是否为目录(Directory)
-e:测试目录或文件是否存在(Exist)
-a:测试目录或文件是否存在(Exist)   
-f:测试是否为文件(File)
-r:测试当前用户是否有权限读取(Read)
-w:测试当前用户是否有权限写入(Write)
-x:测试当前用户是否有权限执行(eXcute)
-L: 测试是否为软连接文件

属性测试补充:
-s FILE #是否存在且非空
-t fd #fd 文件描述符是否在某终端已经打开
-N FILE #文件自从上一次被读取之后是否被修改过
-O FILE #当前有效用户是否为文件属主
-G FILE #当前有效用户是否为文件属组

条件测试:判断某需求是否满足,需要由测试机制来实现,专用的测试表达式需要由测试命令辅助完成

测试过程,实现评估布尔声明,以便用在条件性环境下进行执行

若真,则状态码变量 $? 返回0

若假,则状态码变量 $? 返回1

条件测试命令

例子:

[root@test1 ~]# test -d /etc/sysconfig
[root@test1 ~]# echo $?
0
[root@test1 ~]# test -f /etc/sysconfig
[root@test1 ~]# echo $?
1
[root@test1 ~]# [ -d /etc/sysconfig/ ]            注意前后空格
[root@test1 ~]# echo $?
0
[root@test1 ~]# [ -f /etc/sysconfig/ ]
[root@test1 ~]# echo $?
1
[root@localhost ~]# test -e qiuhe.sh &&echo "yes"
yes
&&表示且的意思 前面的表达式成立才会输出yes

[root@localhost ~]#ll /etc/shadow
---------- 1 root root 1549 10月 19 10:58 /etc/shadow
[root@localhost ~]#[ -r /etc/shadow ]
[root@localhost ~]#echo $?
0
#实际效果,不是表面显示,注意root权限
[root@localhost ~]#[ -x /etc/shadow ]
[root@localhost ~]#echo $?
1
#root执行权限别人没有root也没有,别人有root也有

###a 和 e  的区别 
[root@localhost ~]#[ ! -e /etc/shadow ]
[root@localhost ~]#echo $?
1
[root@localhost ~]#[ ! -a /etc/shadow ]
[root@localhost ~]#echo $?
0
##建议使用e选项
4.2 比较整数数值

[  整数1  -操作符  整数2  ] 公式

  • -eq  :第一个数等于(Equal)第二个数
  • -ne  :第一个数不等于(Not Equal)第二个数
  • -gt   :第一个数大于(Greater Than)第二个数
  • -lt   :第一个数小于(Lesser Than)第二个数
  • -le   :第一个数小于或等于(Lesser or Equal)第二个数
  • -ge   :第一个数大于或等于(Greater or Equal)第二个数
实例:

[  整数1  操作符  整数2  ]
[root@test1 ~]# a=2
[root@test1 ~]# b=3
[root@test1 ~] [ $a -eq $b ]
[root@test1 ~]# echo $?
1
[root@test1 ~]# [ 2 -le 3 ]
[root@test1 ~]# echo $?
0
4.3 字符串比较

常用的测试操作符

  • =:字符串内容相同

  • !=:字符串内容不同,! 号表示相反的意思

  • -z:字符串内容为空

  • -n: 字符是否存在

格式

        [ 字符串1   = 字符串2 ] 是否相同

        [ 字符串1  != 字符串2 ] 是否不相同

        [ -z 字符串 ] 是否为空

        [ -n 字符串 ] 字符是否存在

[root@localhost data]#str1=wang
[root@localhost data]#str2=zhou
[root@localhost data]#[ $str1 = $str2 ]
[root@localhost data]#echo $?
1
#不相同


[root@localhost etc]# [ $USER = root ]&& echo true
true
#当前用户为 root 输出 true

[root@localhost etc]# [ $USER != root ]&& echo true

#当前用户不是 root 输出 true ,否 则输出空行



[root@localhost etc]# read -p "yes/no:" ack
yes/no:
#直接回车

[root@localhost etc]# echo $ack 

[root@localhost etc]# [ -z $ack ] && echo true 
true
#判断 变量ack是否为空 ,如果 是 输出true ,不为 空 输出空行



[root@192 ~]# read -p "是否覆盖当前文件 (yes/no)" ACK
是否覆盖当前文件 (yes/no)yes
[root@192 ~]# [ $ACK = "yes" ] && echo "覆盖"
覆盖
[root@192 ~]# read -p "是否覆盖当前文件 (yes/no)" ACK
是否覆盖当前文件 (yes/no)no
[root@192 ~]# [ $ACK = "no" ] && echo "不覆盖"
不覆盖

4.4 逻辑测试(短路运算)

格式1:[ 表达式1 ] 操作符 [ 表达式2 ] ...

格式2:命令1 操作符 命令2 ...

常见条件:

  • -a  或  && :逻辑与,“而且”的意思全真才为真
  • -o  或  || :逻辑或,“或者”的意思一真即为真
  •  !  :逻辑否(取反)

(1) -a 与  &&

[ $a -ne 1 ]&&[ $a != 2 ]   #变量 a 不等于 1 且 变量 a 不等于 2

       CMD1 && CMD2

一样的效果 ,不同的形式。

[ $a -ne 1 -a  $a != 2 ]    #变量 a 不等于 1 且 变量 a 不等于 2

[[ $a -ne 1 &&  $a != 2 ]]

全真才为真  一假即为假      
第一个CMD1结果为真 ,第二个CMD2必须要参与运算,才能得到最终的结果 
第一个CMD1结果为假 ,总的结果必定为假,因此不需要执行CMD2


[root@localhost data]#a=6

#变量大于等于7 且 变量等于6
[root@localhost data]#[ $a -ge 7 -a  $a -eq 6 ] && echo true || echo false
false

(2)-o  与  ||

                       CMD1           CMD2                   CMD3
[root@localhost etc]# [ 4 -lt 5 ] && echo true     ||     echo false
true
#判断 4 是否小于 5 ,若小于 输出 true (不执行echo false),若不小于 直接执行 echo false。

[root@localhost data]#[ 6 -lt 5 ] &&echo true || echo false
false



[root@localhost data]#a=6

#变量大于等于7 或 变量等于6
[root@localhost data]#[ $a -ge 7 -o  $a -eq 6 ] && echo true || echo false
true


一真即为真,全假才为假
第一个命令为真,不需要执行第二个命令了,  一定为真了
第一个命令为假,需要执行第二个命令来看 整个式子的结果


|| 只有前面不成立时才会执行后面的操作

(3) !    逻辑否(取反)

[root@localhost data]#a=6

#判断变量是否等于 6 ,如果是 则输出true
[root@localhost data]#[ $a -eq 6 ] && echo true || echo false
true

#加了 ! 号后 意思是 把原结果变为 相反的结果 即 正确变为错误 ,错误变成正确。
[root@localhost data]#[ ! $a -eq 6 ] && echo true || echo false
false

其它:

ping 小脚本

[root@localhost data]#vim 2.sh

#!/bin/bash
ping -c 3 -i 0.5 -W2 $1 &> /dev/null && echo "$1 online" || echo "$1 off"
#注意w大写


[root@localhost data]#bash 2.sh 192.168.80.20
192.168.80.20 off

发送qq邮箱

[root@192 ~]#echo test |mail -s test [email protected]
#发送邮件

发送不成功步骤:

shell 脚本_第5张图片

shell 脚本_第6张图片

我们来到 linux 使用 vim /etc/mail.rc 下 进行配置。

[root@localhost data]#vim /etc/mail.rc

set [email protected]                //发件方邮箱或昵称,经过测试必须得和下面的发件邮箱一致,否则发不了邮件
set smtp=smtp.qq.com                       //照着输即可
set [email protected]      //发件邮箱
set smtp-auth-password=asxcjaabxshkcs      //此处使用的就是前面生成的授权码



发送邮件的方法:

#第一种
echo "test" | mail -s 'test linux'  [email protected]


#第二种
mailx -v -s " 主题 " [email protected] < a.sh
//-v可以动态显示邮件发送状态。可以把a.sh文件的内容作为邮件内容发送


磁盘使用量报警脚本

#!/bin/bash
use=$(df -h |grep "/dev/sd" |awk '{print $5}'|awk -F % '{print $1}')
[ $use -gt 80 ]&& echo "磁盘使用量过高" | mail -s diskinfo [email protected] ||echo "磁盘使用量正常"


或者


#!/bin/bash
use=80
disk=`df|grep "sd"|tr -s " "|cut -d " " -f5|cut -d "%" -f1` 
[ $disk -ge $use ] && echo "磁盘使用率过高"|mail -s test [email protected]

判断当前用户是否为root 用户如果是就建立文件夹 ,不是则输出不成功。

[ `whoami` = root -a  ! -e /data/test ] && mkdir -p /data/test ||echo "不成功"

4.5 双中括号 [[  ]]
[[ expression ]] 用法


== 左侧字符串是否和右侧的PATTERN相同
 注意:此表达式用于[[ ]]中,PATTERN为通配符


=~ 左侧字符串是否能够被右侧的正则表达式的PATTERN所匹配
 注意: 此表达式用于[[  ]]中;扩展的正则表达式

用法:

#通配符
[root@centos8 ~]#FILE=test.log
[root@centos8 ~]#[[ "$FILE" == *.log ]]     #判断变量是否以.log结尾
[root@centos8 ~]#echo $?
0
#结果是 0 ,判断结果为正确


[root@centos8 ~]#FILE=test.txt
[root@centos8 ~]#[[ "$FILE" == *.log ]]
[root@centos8 ~]#echo $?
1
#结果是 1 ,判断结果为错误 ,即不是以.log结尾。


[root@centos8 ~]#FILE=test.txt
[root@centos8 ~]#[[ "$FILE" != *.log ]]  #判断是否不以.log 结尾
[root@centos8 ~]#echo $?
0
# 结果为 0 ,此变量不以.log结尾。




#表达式

[root@centos8 ~]#FILE=test.log
[root@centos8 ~]#[[ "$FILE" =~ log$ ]]     #判断变量是否以log结尾
[root@centos8 ~]#echo $?
0
#结果是 0 ,判断结果为正确


[root@centos8 ~]#FILE=test.log
[root@centos8 ~]#[[ "$FILE" =~ ^log ]]     #判断变量是否以log开头
[root@centos8 ~]#echo $?
1
#结果是 1 ,判断结果为错误
4.6  ()  与  {}

(CMD1;CMD2;...)和 { CMD1;CMD2;...; } 都可以将多个命令组合在一起,批量执行

[root@centos8 ~]#( cd /data;ls )
test.log
[root@centos8 ~]#pwd
/root
#开启子shell 不影响当前 bash 环境



[root@centos8 ~]#{ cd /data;ls; }
test.log
[root@centos8 data]#pwd
/data
#不开启子shell 影响当前 bash 环境



[root@localhost data]#name=lc
[root@localhost data]#( name=mcl;echo $name );echo $name
mcl
lc

#可以看到 在括号里定义了一个name=mcl, 因为括号内的内容不影响当前bash环境
#所以 当前环境定义的name=lc 不会发生改变。



[root@localhost data]#name=lc
[root@localhost data]#{ name=mcl;echo $name; };echo $name
mcl
mcl

#因为 {} 里的内容会影响到当前bash环境 ,所以name=lc 被 {} 里的name=mcl 覆盖。
输出的结果都是mcl
5. if 语句的结构

单分支结构

if   判断条件的命令 ; then
则执行命令
fi

你写一个条件 ,如果成功则执行命令,否则直接结束。

#判断 10 是否 大于 5
if  [ 10 -gt  5 ];then          如果判断条件为真 ,执行命令 。否则结束。
执行的命令
fi

双分支结构

if 判断条件的命令;then
执行命令1
else
执行命令2
fi

多分支结构

if 判断条件
then
执行命令1
elif
执行命令2
elif
执行命令3
………
else
执行命令n
fi

例子:

单分支:

[root@localhost data]#vim 1.sh
#!/bin/bash
read -p "输入数字" s

if [ $s -ge 60 ];then    #判断变量是否大于等于 60,结果是 则执行命令,不是 无结果
echo "高"
fi


[root@localhost data]#bash 1.sh
输入数字1
#回车后无结果

[root@localhost data]#bash 1.sh
输入数字60
高

双分支:

###########判断是否为超级管理员#######################
#!/bin/bash
if [ "$USER" != "root" ]
then
    echo "非管理员用户无权限操作"
else
	echo "是管理员用户"
fi



#############判断是主机连通性#######################
#!/bin/bash
ping -c3 192.168.80.145 &>/dev/null
if [ $? = 0 ];then
    echo "网络通顺"
else
    echo "网络不通顺"
fi

多分支:

#######################判断分数 优秀 及格 不及格#######################

#!/bin/bash
read -p "请输入你的考试分数:" grade

if [ $grade -ge 90 ]&& [ $grade -le 100 ]
then
echo "你的成绩为$grade"
echo "你的成绩为优秀"

elif [ $grade -ge 70 ]&&[ $grade -le 89 ]
then
echo "你的成绩为$grade"
echo "你的成绩为良好"

elif
[ $grade -ge 60 ]&&[ $grade -le 69 ]
then
echo "你的成绩为$grade"
echo "你的成绩为合格" 

elif [ $grade -ge 0 ]&&[ $grade -le 59 ]
then
echo "你的成绩为$grade"
echo "你的成绩为不合格"

else
echo "请重新输入"
bash $0
fi
#######################按时间打招呼####################### 

#!/bin/bash
h=`date +%H`
if [ $h -ge 6 -a $h -le 10 ];then
echo "早上好"
elif [ $h -ge 11 -a $h -le 13 ];then
echo "中午好"
elif [ $h -ge 14 -a $h -le 18 ];then
echo "下午好"
else
echo "晚上好"
fi
6. case 

判断一个变量的不同取值

格式:

case  变量值  in
模式1)
  命令
;;
模式2)
  命令
;;
.......
*)
  命令
;;
esac

case 支持 glob 风格的通配符:
*    任意长度任意字符
?    任意单个字符
[0-9]  指定范围内的任意单个字符
|     或者,如: a|b

例子:

######################小心情#################################
#!/bin/bash
read -p "你今天心情好吗?(yes/no)" INPUT
INPUT=`echo $INPUT|tr [A-Z] [a-z]`

case $INPUT in
y|ye|es|yes)
        echo "看来你心情不错。"
;;
n|no|o)
        echo "x-x"
;;
*)
        echo "@-@"
        bash $0
esac

#!/bin/bash
read -p "你今天心情好吗?(yes/no)" INPUT
INPUT=`echo $INPUT|tr [A-Z] [a-z]`

case $INPUT in          #对INPUT变量进行判断
y|ye|es|yes)
        echo "看来你心情不错。"
;;
n|no|o)
        echo "x-x"
;;
*)
        echo "@-@"
        bash $0
esac



或者用另一种方法


#!/bin/bash
read -p "你今天心情好吗?(yes/no)" INPUT

case $INPUT in
[yY]|[yY][eE]|[eE][sS]|[yY][eE][sS])     #y,ye,es,yes 的大小写组合都会被匹配
        echo "看来你心情不错。"
;;
[nN]|[oO]|[nN][oO])               #n,o,no 的大小写组合都会被匹配
        echo "x-x"
;;
*)                                #经过以上判断后 ,无法被匹配的将重新输入一次
        echo "@-@"
        bash $0
esac

shell 脚本_第7张图片

石头,剪刀,布

#!/bin/bash
m=`echo $[RANDOM%3+1]`
read -p "请输入 1.石头2.剪刀3.布:" h


if [ $m -eq $h ]
then
echo  "打成平局"
bash $0
elif [ $h -eq 1 -a  $m -eq 2 -o $h -eq 2 -a $m -eq 3 -o $h -eq 3  -a $m -eq 1 ]
then
echo "你赢了"
else
echo "你输了"
fi

其它实验:

写一个 检查 80 端口 是否开启,如果开启则反馈 httpd 正在运行 否则 启动httpd服务。

[root@localhost data]#vim httpd.sh

#!/bin/bash
systemctl status httpd &> /dev/null
if [ $? -eq 0 ];then
  echo "httpd 服务正在运行"
  else
  systemctl start httpd &> /dev/null
     if [ $? -eq 0 ];then
       echo "httpd 服务已开启"
       else
       echo "httpd 服务可能未安装或端口被占用"
     fi
fi

安装 httpd 服务 ,先判断当前操作系统 ,如果是 centos 使用yum 安装。如果是 ubuntu 使用apt

。若其它系统 不支持。

#!/bin/bash
if grep -q centos /etc/os-release;then
  yum install -y httpd &> /dev/null
  if [ $? -eq 0 ];then
    echo "安装成功"
    else
    echo "安装失败,请检查"
  fi
elif grep -q ubuntu /etc/os-release;then
  apt install apache2 -y &> /dev/null
  if [ $? -eq 0 ];then
    echo "安装成功"
    else
    echo "安装失败,请检查"
  fi
else
echo "您的操作系统不支持"
fi

你可能感兴趣的:(linux,运维,服务器)