shell


为什么要用shell?

1.---
安装操作系统(CentOS)自动化安装操作系统(kickstart cobbler)底层shell

2.--- 初始化、优化操作系统
	1)ntp时间同步
	2)更改默认yum源
	3)ssh优化
	4)关闭Selinux
	5)关闭/开启 防火墙(C6:iptables C7:firewalld)
	6)安装基础服务(wget vim lrzsz net-tools unzip gzip...)
	7)优化文件描述符
	8)优化字符集
	0)。。。

3.--- 安装服务
	1)Nginx
	2)PHP
	3)MySQL 
	4)Redis
	5)MHA
	6)Rsync
	7)NFS
	8)MongoDB
	9)Zabbix
	...
	
4.--- 启动服务(系统默认的shell脚本)
5.--- 脚本实现自动化代码上线
6.--- 监控服务(使用shell)
7.--- 结合定时任务使用shell

8.--- 重复性工作写入脚本
	1)日志切割
	2)日志分析
	3)数据统计
	4)机器巡检
	5)数据备份
	...

shell编程和基础知识

1)熟练使用vim编辑器
2)熟悉ssh终端(Xshell、CRT)
3)熟练掌握linux常用命令
4)熟练掌握linux正则表达式及三剑客命令

学习shell三部曲

先读懂shell
再修改shell
自己写shell

什么是shell?

image-20240703133201570

##### 交互式shell #####
交互式模式就是shell等待你的输入,并且执行你提交的命令。这种模式被称作交互式是因为shell与用户进行交互。这种模式也是大多数用户非常熟悉的:登录、执行一些命令、签退。当你签退后,shell也终止了。
##### 非交互式shell #####
shell也可以运行在另外一种模式:非交互式模式。在这种模式下,shell不与你进行交互,而是读取存放在文件中的命令,并且执行它们。当它读到文件的结尾,shell也就终止了。

shell脚本的执行方式

## 需要加上执行权限x  可以直接执行
./1.sh

## 正常执行
sh 1.sh
bash 1.sh
. 1.sh
source 1.sh

## 区别 ##
使用sh 1.sh和bash 1.sh在新的子Shell中执行脚本,当前Shell环境不受影响。
使用. 1.sh和source 1.sh在当前Shell中执行脚本,当前Shell环境会被脚本中的变化影响。

[root@GIAO ~]# cat 1.sh 
#!/bin/bash
cd /tmp && ls -l 
[root@GIAO ~]# sh 1.sh 
total 0
drwx------. 2 root root 6 Jul  3 09:37 vmware-root_657-4022112241
[root@GIAO ~]# . 1.sh 
total 0
drwx------. 2 root root 6 Jul  3 09:37 vmware-root_657-4022112241

shell的脚本规范

1.目录统一
2.shell脚本的结尾要以.sh结尾
3.脚本的开头需要有解释器
#!/bin/bash
4.脚本中需要有作者信息
#!/bin/bash
#Author: _DriverL_
#Date: _1999-12-21_
#Name: _Print Message_
5.必须要加注释(开发规范,运维规范...)
6.shell中的文字尽量使用英文
7.成对的符号和语句一次性书写完

什么是shell脚本

# 1.首先先编辑一个模板文件,该模板文件可以叫任何名字
[root@zabbix01 ~]# vim /usr/share/vim/vimfiles/template.zls
#!/bin/bash
# 2.写完之后,我们需要修改一下vim的配置文件
[root@zabbix01 ~]# vim /etc/vimrc
autocmd BufNewFile *.spec 0r /usr/share/vim/vimfiles/template.spec
## 在第28行,autocmd自动保存模板文件,修改一下,因为我们是要写shell脚本的模板
## 所以我们要把*.spec 修改成*.sh
## 然后将后面的模板文件改成你定义的模板文件名
autocmd BufNewFile *.sh 0r /usr/share/vim/vimfiles/template.zls
# 3.接下来,我们编辑所有只要以sh结尾的文件,都会带有作者信息
[root@zabbix01 ~]# vim test_zls.sh
####################### 华丽的分割线 ########################
给你们介绍一个更牛逼的东西,叫做vimscript,专门的vim脚本语法。
我们可以完全自定义,上面那个模板不识别变量,你写啥就是啥。
接下来我们自己写一个,因为我要获取日期,上面的模板还得我们手动添加。
所以我就简单研究了一下vimscript里面的函数,吭哧瘪肚的写出来下面的内容
vim ~/.vimrc
autocmd bufNewFile *.py,*.sh,*.java exec ":call SetTitle()"
func SetTitle()
	if expand("%:e") == 'sh'
		call setline(1, "#!/bin/bash")
		call setline(2, "# File Name: ".expand("%"))
		call setline(3, "# Version: v1.1 ")
		call setline(4, "# Author: DriverZeng")
		call setline(5, "# Mail: [email protected] ")
		call setline(5, "# Domain: www.oldboydeu.com ")
		call setline(6, "# Date Time: ".expand(strftime("%Y-%m-%d %H:%M")))
	endif
endfunc
autocmd bufNewFile *.py,*.sh,*.java exec ":call SetTitle()"
func SetTitle()
	if expand("%:e") == 'sh'
		call setline(1, "#!/bin/bash")
		call setline(2, "")
		call setline(3, "# File Name: __".expand("%") . "__")
		call setline(4, "# Version: __v1.1__ ")
		call setline(5, "# Author: __DriverZeng__ ")
		call setline(6, "# Mail: [email protected]__ ")
		call setline(7, "# Blog: __https://blog.driverzeng.com__ ")
		call setline(8, "# DateTime: __".expand(strftime("%Y-%m-%d %H:%M")) .
"__")
	endif
endfunc
如果想要学习vimscript编程,请前往:WC3School一个很牛逼的网站,前端语言,后端语言,都可以学习。

开发语言中程序代码的分类

解释型
	脚本在运行时解释器逐行解释并执行
编译型
	源代码在运行之前先经过编译器的编译过程
	生成二进制的代码
	生成完再运行
# 编译型
vim hello.c
#include <stdio.h>
void main(){
	printf("hello world");
}
# 编译成二进制文件
gcc hello.c -o hello.bin
# 执行脚本
[root@m01 scripts]# ./hello.bin
hello world

变量

变量介绍

变量即变化的量,核心是“变”与“量”二字,变即变化,量即衡量状态。
name=zls
age=18
例如:
1.在游戏中,英雄的等级为1,打怪升级(变)10级
2.游戏中人物名字:Driver_Zeng,使用了改名卡,变为:曾老湿
3.植物大战僵尸:僵尸的存活状态为True,被植物打死了,变为False

变量的使用

vim test.sh
#!/bin/bash
name=zls
age=18
echo $name
echo $age

[root@m01 ~]# sh test.sh
zls
18
name	 = 		lisi
变量名 赋值符号   变量值
定义变量的语法(分三部分):
1)变量名
相当于一个门牌号,便于取出变量值,是访问到值的唯一方式
2)赋值符号
将值的内存地址,绑定给变量名
3)变量值
用来表示状态
变量的使用规则:先定义,在通过变量名去引用。

变量的规范

1.大前提:变量名的命名应该能够反映出值记录的状态。

2.变量名只能是 字母、数字或下划线的任意组合(区分大小写)
#!/bin/bash
x=1
X=2
echo $X
	2.变量名的第一个字符不能是数字
	[root@m01 scripts]# cat 4.sh
	#!/bin/bash
	1x=1
	1X=2
	echo $1X
	[root@m01 scripts]# sh 4.sh
	4.sh: line 2: 1x=1: command not found
	4.sh: line 3: 1X=2: command not found
	X
尽量不要使用命令设置变量

定义变量名的方式

###  1.下划线(纯小写)
#!/bin/bash
stu_name=zhangsan
echo $stu_name

###  2.驼峰体
#!/bin/bash
Stu_Of_Name=zhangsan
echo $Stu_Of_Name

3.不好的方式
	1)变量名为中文、拼音
	2)变量名过长
	3)变量名词不达意

变量的分类

###### 环境变量 ####
# 查看方式
env
declare
export

# 重点记住
LANG # 当前系统使用的语言
PATH # 记录可执行文件的路径
PS1 # 记录命令提示符的格式
UID # 记录当前登录用户的uid
HOSTNAME # 记录当前主机名

# 了解历史相关变量
HISTSIZE 											# 历史记录能记录1000条
HISTFILESIZE										 # 历史记录文件能记录1000条
HISTFILE 											# 历史命令记录的文件路径和文件名
TMOUT 										# 设置超时时间 如果一定的时间内没有操作就会退出登录 /etc/profile source
HISTCONTROL: export HISTCONTROL=ignorespace 			# 离职专用变量 任何命令前面加上空格都不
会被history记录 /etc/profile source
PROMPT_COMMAN 										# 跳板机专用变量 记录上一条命令

特殊变量-位置

符号 含义 应用
$0 脚本名字 脚本使用方法常用:给出错误提示或者使用帮助
$n 脚本的第n个参数 命令传参,传递给脚本,在脚本中使用
$# 传递给脚本的参数个数 判断脚本是否传参
$* 获取脚本所有的参数 将所有参数当成是一个整体,对传递的参数进行判断
$@ 获取脚本中所有的参数 将每一个参数当成是一个整体,对传递的参数进行判断
$0
记录脚本名称
$n
#!/bin/bash
echo $1
echo $2
echo $3
[root@m01 ~]# sh 1.sh 1 2 3 4 5 6 7 8 9 10 11 12
1 2 3 4 5 6 7 8 9 10 11 12
[root@m01 ~]# sh 1.sh a b c d e f g h i j k l
a b c d e f g h i a0 a1 a2

# 规范书写
#!/bin/bash
echo $1 $2 $3 $4 $5 $6 $7 $8 $9 ${10} ${11} ${12}

# $@ 和 $*的区别
#!/bin/bash
echo '$@输出结果如下'
for num1 in "$@";do
echo $num1
done
echo '$*输出结果如下'
for num2 in "$*";do
echo $num2
done

特殊变量-状态

符号 含义 应用
$? 上一条命令的返回值 判断命令执行的是否成功
$$ 当前脚本允许的pid 在脚本允许时将pid记录到文件中,方便kill
$! 上一个运行脚本的pid
$_ 上一个命令或者脚本的最后一个参数 类似于esc + .
# $?
作用上一条命令的返回值
注意点:并不是所有的命令成功执行都为0
# 另类
false
diff

变量子串(复杂,但实用)

这个东西,怎么说呢,比较复杂,不容易学懂,比较高级,很实用,但是不用行不行呢,有些时候可以用其他命令来代替,有些时候,就够呛了。当然,如果学不会这个,使用其他命令也可以,比如sed 、awk 、grep这几个组合也能实现功能,但是效率肯定没有直接使用子串的效率高。
语法 含义
${变量名} 调用变量
${#变量名} 变量的长度
${变量名:offset} 截取变量,从offset之后
${变量名:offset:length} 截取变量,从offset之后取指定长度
${变量名#word} 从变量开头,删除最短匹配word的子串
${变量名##word} 从变量开后,删除最长匹配的word子串
${变量名%word} 从变量结尾,删除最短匹配的word的子串
${变量名%%word} 从变量结尾,删除最长匹配的word的子串
${变量名/pattern/string} 使用string替换第一个pattern
${变量名//pattern/string} 使用string替换全局的pattern
# ${变量名}------------调用变量
	[root@m01 ~]# cat 1.sh
	#!/bin/bash
	num=$1
	echo ${num}

# ${#变量名}----------统计变量长度
	[root@m01 ~]# cat 1.sh
	#!/bin/bash
	num=$1
	echo ${#num}
[root@m01 ~]# sh 1.sh 123456
6
[root@m01 ~]# sh 1.sh 1234567
7
[root@m01 ~]# sh 1.sh 12345678
8

# ${变量名:偏移量:步长} ------ 取变量中的n位字符
[root@m01 ~]# cat 1.sh
#!/bin/bash
geming='xiangtianjie500'
echo ${geming}
echo ${geming:4:2}
[root@m01 ~]# sh 1.sh
xiangtianjie500
gt
[root@m01 ~]# vim 1.sh
[root@m01 ~]# sh 1.sh
xiangtianjie500
gti

# ${变量名#字符串} ------ 从变量开头,删除最短匹配word的子串
[root@GIAO ~]# cat 6.sh
#!/bin/bash
baby=aaa/bbb/ccc/ddd/eee/fff
echo ${baby#*/}
[root@GIAO ~]# sh 6.sh 
bbb/ccc/ddd/eee/fff

[root@GIAO ~]# cat 6.sh
#!/bin/bash
baby=aaa/bbb/ccc/ddd/eee/fff
echo ${baby#*ccc/}
[root@GIAO ~]# sh 6.sh 
ddd/eee/fff

# ${变量名##字符串} ------ 从变量开头,删除最长匹配word的子串
[root@GIAO ~]# cat 6.sh
#!/bin/bash
baby=aaa/bbb/ccc/ddd/eee/fff
echo ${baby##*/}
[root@GIAO ~]# sh 6.sh 
fff

# ${变量名%字符串} ------ 从变量结尾,删除最短匹配的word的子串
[root@GIAO ~]# cat 6.sh
#!/bin/bash
baby=aaa/bbb/ccc/ddd/eee/fff
echo ${baby%/ddd*}
[root@GIAO ~]# sh 6.sh 
aaa/bbb/ccc

# ${变量名%%字符串} ------ 从变量结尾,删除最长匹配的word的子串
[root@GIAO ~]# cat 6.sh
#!/bin/bash
baby=aaa/bbb/ccc/ddd/eee/fff
echo ${baby%%/*}
[root@GIAO ~]# sh 6.sh 
aaa

# ${变量名/原内容/替换内容} ------ 使用string替换第一个pattern
[root@GIAO ~]# cat 6.sh
#!/bin/bash
baby=aaa/bbb/ccc/ddd/eee/ff/aaa/ccc/dddaaa
echo ${baby/aaa/ggg}
[root@GIAO ~]# sh 6.sh 
ggg/bbb/ccc/ddd/eee/ff/aaa/ccc/dddaaa


# ${变量名//原内容/替换内容} ------ 使用string替换所有的pattern
[root@GIAO ~]# cat 6.sh
#!/bin/bash
baby=aaa/bbb/ccc/ddd/eee/ff/aaa/ccc/dddaaa
echo ${baby//aaa/ggg}
[root@GIAO ~]# sh 6.sh 
ggg/bbb/ccc/ddd/eee/ff/ggg/ccc/dddggg
[root@GIAO ~]# 

拓展变量

写法 含义
${parameter:-string} 如果parameter没被赋值或其值为空,就以string作为默认值
${parameter:=string} 如果parameter没被赋值或其值为空,就以string作为默认值,并将string赋值给parameter
${parameter:?string} 如果parameter没被赋值或其值为空,就以string作为错误输出,否则显示parameter内容
${parameter:+string} 如果parameter没被赋值或其值为空,就什么都不做,否则用string替换变量内容
# ${变量名:-字符串}
# 如果parameter没被赋值或其值为空,就以string作为默认值
echo ${name:-haha}

# 前提是如果parameter没被赋值或其值为空
[root@m01 ~]# echo $name
[root@m01 ~]# echo ${name:-haha}
haha

# 前提该变量名已经被赋值 则无法将string作为他的默认值
[root@m01 ~]# name=xxx
[root@m01 ~]# echo $name
xxx
[root@m01 ~]# echo ${name:-haha}
xxx

# ${变量名:=字符串}
# 如果parameter没被赋值或其值为空,就以string作为默认值,并将string赋值给parameter
[root@m01 ~]# echo $name
[root@m01 ~]# echo ${name:=haha}
haha
[root@m01 ~]# echo ${name:=haha}
haha
[root@m01 ~]# echo $name
haha

# ${变量名:?字符串}
# 如果parameter没被赋值或其值为空,就以string作为错误输出,否则显示parameter内容
[root@m01 ~]# echo ${name:?haha}
-bash: name: haha
[root@m01 ~]# echo $?
1
[root@m01 ~]# name=xxx
[root@m01 ~]# echo ${name:?haha}
xxx

# ${变量名:+字符串}
# 如果parameter没被赋值或其值为空,就什么都不做,否则用string替换变量内容
[root@m01 ~]# name=xxx
[root@m01 ~]# echo ${name:+haha}
haha
[root@m01 ~]# echo $name
xxx