shell编程
初始shell
程序 语言 编程
----------------------------------
语言
自然语言:汉语、英语
计算机语言:c语言、c++、(java php python go shell)
编译型语言 c c++ java
解释型语言 php python bash
编译型语言:编译型语言的首先将源代码编译生成机器语言,再由机器运行机器码(二进制)。像C/C++等都是编译型语言。
解释型语言:源代码不是直接翻译成机器语言,而是先翻译成中间代码,再由解释器对中间代码进行解释运行。比如Python/JavaScript/Shell等都是解释型语言
c 编译型执行代码需要编译成cpu能认识的二进制码 x86指令集
java 编译型执行编译-->字节码,cpu不能直接运行,只能被Java虚拟机执行
shell 解释型语言执行 慢
shell 定义
Shell 也是一种程序设计语言,它有变量,关键字,各种控制语句,有自己的语法结构,利用shell程序设计语 言可以编写功能很强、代码简短的程序。
shell是外壳的意思,就是系统的外壳,我们可以通过shell的命令来控制和操作操作系统,比如linux中的shell命令就包括ls、cd、pwd等等,总结来说shell就是一个命令解释器,他通过接收用户输入的shell命令来启动、停止程序的运行或者对计算机进行控制。
shell的分类和切换
[root@linux-server ~]
/bin/sh
/bin/bash
/sbin/nologin
/usr/bin/sh
/usr/bin/bash
/usr/sbin/nologin
查看当前正在使用的shell
[root@linux-server ~]
/bin/bash
shell的修改
使用场景
Shell 能做什么?
1. 自动化批量系统初始化程序 (update,软件安装,时区设置,安全策略...)
2. 自动化批量软件部署程序 (LAMP,LNMP,Tomcat,LVS,Nginx)
3. 应用管理程序 (KVM)
4. 日志分析处理程序(PV, UV, 200, !200,grep/awk)
5. 自动化备份恢复程序(MySQL完全备份/增量 + Crond)
6. 自动化信息采集及监控程序(收集系统/应用状态信息,CPU,Mem,Disk,Net,TCP Status,Apache,MySQL)
7. 配合Zabbix信息采集(收集系统/应用状态信息,CPU,Mem,Disk,Net,Apache,MySQL)
8. 9*9乘法表、俄罗斯方块,打印三角形,打印圣诞树,打印五角星,运行小火车,坦克大战,排序实现
9. Shell可以做任何运维的事情(一切取决于业务需求)
shell 特性回顾
文件描述符与输出重定向:
在 shell程序中,最常使用的FD (file descriptor) 大概有三个, 分别是:
0: Standard Input (STDIN)
1: Standard Output (STDOUT)
2: Standard Error Output (STDERR)
在标准情况下, 这些FD分别跟如下设备关联:
stdin(0): keyboard 键盘输入,并返回在前端
stdout(1): monitor 正确返回值 输出到前端
stderr(2): monitor 错误返回值 输出到前端
>a.txt
1>a.txt
2>a.txt
&>a.txt
1>&2
2>&1
一般来说, "1>" 通常可以省略成 ">".
1>&2 正确返回值传递给2输出通道 &2表示2输出通道,之前如果有定义标准错误重定向到某log文件,那么标准输出也重定向到这个log文件,如果此处错写成 1>2, 就表示把1输出重定向到文件2中.
2>&1 错误返回值传递给1输出通道, 同样&1表示1输出通道.
例子. 当前目录下只有a.txt,没有b.txt
[root@linux-server ~]
[root@linux-server ~]
[root@linux-server ~]
ls: cannot access b.txt: No such file or directory
a.txt
现在, 正确的输出和错误的输出都定向到了file.out这个文件中, 而不显示在前端 =================================
[root@linux-server ~]
> ni hao a haha
> !
[root@linux-server ~]
ni hao a haha
注:这里也可以使用EOF需要成对使用即可!
bash 初始化
用户登录时相关的bash配置文件 (登录脚本)属于全局配置文件:
/etc/profile
/etc/profile.d/*.sh
/etc/bashrc
个人配置文件,每个用户的配置文件
~/.bsah_profile
~/.bashrc
用户登录时加载bash配置文件的过程:
登录式shell加载配置文件过程
~/.bash_profile --> ~/.bashrc --> /etc/bashrc ---> /etc/profile --> /etc/profile.d/*.sh
非登录式shell加载配置文件过程
~/.bashrc --> /etc/bashrc --> /etc/profile.d/*.sh
下面的文件为系统的每个用户设置环境信息Shell设置文件:
/etc/profile(系统级)启动时执行
这是系统最主要的shell设置文件,也是用户登陆时系统最先检查的文件,有关重要的环境变量都定义在此,其中包括 PATH,USER,MAIL,HOSTNAME,HISTSIZE,INPUTRC等。而在文件的最后,它会检查并执行/etc/profile.d/*.sh的脚本。
~/.bash_login(用户级)登录时执行
如果~.bash_profile文件不存在,则系统会转而读取.bash_login这个文件内容。这是用户的登陆文件,在每次用户登陆系统时,bash都会读此内容,所以通常都会将登陆后必须执行的命令放在这个文件中。
~/.bash_logout 离开时执行如果想在注销shell前执行一些工作,都可以在此文件中设置。 例如:
clear
仅执行一个clear命令在你注销的时候
~/.bash_history(用户级)
补全 tab键
历史 ---history
别名 --alias
快捷键 --ctrl+c \ ctrl+z\ ctrl+l
前后台作业 ---jobs---bg ---fg
重定向 ---> >> 2> 2>> < &
管道 -----|
&&:逻辑与,前面执行成功,后面才执行。前面命令执行失败,后面命令也不执行
||:逻辑或,前面执行失败,后面执行,前面命令执行成功,后面不执行。
;:从左往右按顺序执行,不管前面执行成功与否,后面都执行
通配符:[] {} ? *
正则表达式 脚本
查看历史命令
[root@linux-server ~]
调用历史命令
上下健
!关键字
!历史命令行号
!! 执行上一条命令
!$ 上一条命令的最后一个参数
esc . 上一条命令的最后一个参数
Ctrl+r 在历史命令中查找,输入关键字调出之前的命令
别名
查看别名
[root@linux-server ~]
设置别名
临时设置
[root@linux-server ~]
[root@linux-server ~]
88
永久设置
小小技巧:显示历史命令执行时间
1.设置变量:
[root@linux-server ~]
HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S"
[root@linux-server ~]
2.再次执行history查看结果
Ctrl+a 切换到命令行开始(跟home一样,但是home在某些unix环境下无法使用)
Ctrl+u 清除剪切光标之前的内容
Ctrl+k 清除剪切光标之后的内容
ctrl+y 粘贴刚才所删除的字符
Ctrl+r 在历史命令中查找,输入关键字调出之前的命令
Ctrl+l 清屏
Ctrl+c 终止
Ctrl+e 切换到命令行末尾
在 Shell命令中,通常会使用通配符表达式来匹配一些文件
*,?,[],{}
例:
字符 含义 实例
* 匹配 0 或多个字符 a*b a与b之间可以有任意长度的任意字符, 也可以一个也没有, 如aabcb, axyzb, a012b, ab。
? 匹配任意一个字符 a?b a与b之间必须也只能有一个字符, 可以是任意字符, 如aab, abb, acb, a0b 。
[list] 匹配 list 中的任意单一字符 a[xyz]b a与b之间必须也只能有一个字符, 但只能是 x 或 y 或 z, 如: axb, ayb, azb 。
[!list] 匹配 除list 中的任意单一字符 a[!0-9]b a与b之间必须也只能有一个字符, 但不能是阿拉伯数字, 如axb, aab, a-b。
[c1-c2] 匹配 c1-c2 中的任意单一字符 如:[0-9] [a-z] a[0-9]b 0与9之间必须也只能有一个字符 如a0b, a1b... a9b。
{string1,string2,...} 匹配 sring1 或 string2 (或更多)其一字符串 a{abc,xyz,123}b a与b之间只 能是abc或xyz或123这三个字符串之一。
[root@linux-server tmp]
[root@linux-server tmp]
[root@linux-server tmp]
a012b aabcb ab acb axyzb
[root@linux-server tmp]
a012b aabcb ab acb axyzb
[root@linux-server tmp]
acb
[root@linux-server tmp]
[root@linux-server tmp]
[root@linux-server tmp]
axb axyb ayb azb
[root@linux-server tmp]
axb ayb
[root@linux-server tmp]
azb
[root@linux-server tmp]
ayb azb
[root@linux-server tmp]
[root@linux-server tmp]
[root@linux-server tmp]
a0b a1b a9b
[root@linux-server tmp]
[root@linux-server tmp]
[root@linux-server tmp]
ls: cannot access a{abc}b: No such file or directory
[root@linux-server tmp]
aabcb axyzb
[root@linux-server tmp]
a012b aabcb axyzb
shell 脚本规范
[root@linux-server ~]
拓展名后缀,如果省略.sh则不易判断该文件是否为shell脚本
1.
2.
3.
4.
5.
6.
7. printf "hello world\n"
功能说明:打印hello world
[root@linux-server ~]
hello world
[root@linux-server ~]
[root@linux-server ~]
[root@linux-server ~]
hello world
第一行: “
第二行: 为“
第七行: 为格式化打印语句printf, printf可以把后面的“hello world”打印到指定的终端中, \n 为换行符
bash 脚本测试:
1.这将执行该脚本并显示所有变量的值
[root@linux-server ~]
+ printf 'hello world\n'
hello world
2.不执行脚本只是检查语法模式,将返回所有错误语法
[root@linux-server ~]
3.执行脚本前把脚本内容显示在屏幕上
[root@linux-server ~]
printf "hello world\n"
hello world
变量的类型
变量:bash作为程序设计语言和其它高级语言一样也提供使用和定义变量的功能,简单说就是让一个特定的字符串代表不固定的内容 a=123,echo $a
预定义变量:预定义的特殊变量有着特殊的含义,用户不可以更改,所有的预定义变量都由$符号和另外一个符号组成,常用的预定义特殊变量如下:
$$ 当前进程PID
$? 命令执行后的返回状态.0 为执行正确,非 0 为执行错误
$
$* 所有位置参数的内容
$@ 显示所有的参数
$! 上一个后台进程的PID (wait命令中使用,后面讲)
拓展:$* 和 $@ 有什么区别
[root@linux-server ~]
[root@linux-server ~]
[root@linux-server ~]
[root@linux-server ~]
anaconda-ks.cfg a.txt b.txt file.out helloworld.sh
[root@linux-server ~]
0
[root@linux-server ~]
echo "The current process is $$"
[root@linux-server ~]
The current process is 1416
[root@linux-server ~]
[1] 1418
[root@linux-server ~]
1418
自定义变量:就是自己设置的变量只能在当前终端和脚本中使用
变量名称=值
= 赋值符号 前后不能有空格 ;
值: 所有的字符串和数字都可以;
引用变量: $变量名 或 ${变量名}。
示例:
[root@linux-server ~]
[root@linux-server ~]
100
[root@linux-server ~]
[root@linux-server ~]
100a
查看变量: echo $变量名
取消变量: unset 变量名,仅在当前shell中有效
环境变量:shell在开始执行时已经定义好的,就是系统执行环境的一些设置
export 变量名
临时生效
[root@linux-server ~]
[root@linux-server ~]
192.168.1.1
永久生效
写到4个登陆脚本中 ~/.bashrc ~/profile 更好放在/etc/profile.d/目录下建立独立的环境变量配置文件
[root@linux-server ~]
IPADDT=192.168.1.1
[root@linux-server ~]
[root@linux-server ~]
192.168.1.1
常用环境变量:USER UID HOME HOSTNAME PWD PS1 PATH
PATH:存储所有命令所在的路径
[root@localhost ~]
[root@localhost ~]
[root@localhost ~]
[root@localhost ~]
hello
[root@localhost ~]
exit
[root@localhost ~]
exit
[root@localhost ~]
**实战**
```shell
编写一个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
现在的时间是: 2020-08-22-17:34:03
当前的用户是: root
当前的用户标识: 0
当前的主机名称是: linux-server
当前可用网卡IP是: 192.168.246.148/24
##脚本源码如下
#!/usr/bin/bash
# 获取主机基本信息
time=`date +%F-%T`
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}'
489M
取当前系统剩余内存:
[root@linux-server script]# echo "现在的剩余内存是:"`free -m |awk 'NR==2{print $4}'`
现在的剩余内存是:16G
取当前cpu平均负载:
[root@linux-server script]# echo 现在cpu的`uptime | cut -d, -f4-` #-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]
centime=`date '+%Y-%m-%d %H:%M:%S'`
nowtime=`uptime |awk '{print $1}'`
username=$USER
verage=`uptime |awk -F',' '{print $3,$4,$5}'`
myname=xuange
cat >>file1.txt <<EOF
echo "时间:$centime"
echo "系统的当前时间是: $nowtime"
echo "系统当前负载: $verage"
echo "当前的用户是: $username"
echo "我的名字是: $myname"
EOF
[root@linux-server script]
[root@linux-server script]
[root@linux-server script]
===========================================================
位置变量也叫位置参数:在脚本代码中调用通过命令行传递给脚本的参数
$1 $2 $3 $...
例子:
例子:
[root@linux-server ~]
[root@linux-server script]
echo 我的第一个位置参数是:$1
echo 我的第二个位置参数是:$2
echo 我的第三个位置参数是:$3
echo 我的第四个位置参数是:$4
echo 一共有 $
echo 你输入的参数分别是:$*
[root@linux-server script]
[root@linux-server script]
我的第一个位置参数是:1
我的第二个位置参数是:3
我的第三个位置参数是:4
我的第四个位置参数是:6
一共有 4 个位置参数
你输入的参数分别是:1 3 4 6
变量运算
算式运算符: +、-、*、/、()、%取余(取模)
如:(5+3)*2
运算方式:$(()) $[] expr
$(()):例子
6
1
$[]:例子
6
expr:例子
8
乘法运算:
[root@linux-server script]
15
[root@linux-server script]
15
脚本案例:
[root@localhost ~]
a=2
b=13
echo "$a和$b的和是: $(( $a + $b ))"
echo "$a乘$b的值是: $(( $a * $b ))"
echo "$a和$b的差是: $(( $a - $b ))"
echo "$a和$b的商是: $(( $b / $a ))"
echo "$a和$b的余是: $(( $b % $a ))"
echo "$a和$b的和是: `expr $a '+' $b`"
=================================================================
取1到6之间的随机数:
5
取1-10之间的随机数:
5
[root@linux-server script]
echo $(($RANDOM % 50 + 1 ))
这串代码特别简单,就是利用RANDOM这个随机数生成器进行取余就能够实现,至于为什么取余时需要+1是因为在取余时如果被整除那么余数会是0,这样就不在限定范围内了
浮点运算
bash本身不能做小数计算:需要bc命令转换
变量引用
1.当一个字符被引用时,其特殊含义被禁止,把有意义的变的没意义,把没意义的变的有意义
转义案例:
[root@linux-server script]
you now 250
[root@linux-server script]
you now $1250
完全引用:''
部分引用:""
例子:
[root@linux-server script]
[root@linux-server script]
1901班有1个女生
[root@linux-server script]
1901班有1个女生
[root@linux-server script]
1901班有$num个女生
脚本例子
echo 'echo $USER' >> b.sh
======================================================
read:功能就是读取键盘输入的值,并赋给变量
read后面的变量var可以只有一个,也可以有多个,这时如果输入多个数据,则第一个数据给第一个变量,第二个数据给第二 个变量,如果输入数据个数过多,则最后所有的值都给最后一个变量
read -p "请输入你的用户名和密码还有你的年龄: " name pass age
echo "你的名字是 $name"
echo "你的密码是 $pass"
echo "你的年龄是 $age"
案例1:
[root@linux-server script]
read -p "请输入你的银行卡帐号" num
read -p "请在五秒内输入密码" -t 5 pass
echo "你的密码错误!"
[root@linux-server script]
案例2:
[root@linux-server script]
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]
[root@linux-server script]
案例3:
[root@linux-server script]
read -s -p "Enter your password: " pass
echo "your password is $pass"
exit 1
[root@linux-server script]
[root@linux-server script]
========================================================
取消屏幕回显
[root@linux-server script]
[root@linux-server script]
显示变量长度
[root@linux-server script]
[root@linux-server script]
3
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
练习:
[root@linux-server script]
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
;;
*)
echo "你输入的不正确!请按提示输入"
;;
esac
[root@linux-server script]
[root@linux-server script]
脚本运行
创建bash脚本(shell脚本)
1.创建脚本文件
指定命令解释器
注释
编写bash指令集合
2.修改权限
bash脚本执行
变量置换
命令替换
取命令结果用。把命令的结果拿出来
[root@linux-server ~]
[root@linux-server ~]
1225
[root@linux-server ~]
[root@linux-server ~]
12-25
变量替换
一 ${parameter:-word}
若 parameter 为空或未设置,则用 word 代替 parameter 进行替换,parameter 的值不变,若 parameter 不为空,则不替换,parameter 的值不变
-3
二 ${parameter:=word}
若 parameter 为空或未设置,则用 word 代替 parameter 进行替换,parameter 的值改变,若 parameter设置了,则不替换,parameter的值不变
3
3
三 ${parameter:+word}
若 parameter 设置了值,则用 word 代替 parameter 进行替换,parameter 的值不变
3
2
脚本案例
[root@localhost ~]
read -p "请选择你要安装的mysql版本,默认为5.7 " var
echo "默认的mysql版本为${var:-5.7}"
echo "你选择的版本是$var"
变量替换-匹配截取
语法:
${变量
${变量
${变量%关键词} 若变量内容从尾向前的数据符合『关键词』,则将符合的最短数据切除
${变量%%关键词} 若变量内容从尾向前的数据符合『关键词』,则将符合的最长数据切除
${变量/旧字符串/新字符串} 若变量内容符合『旧字符串』则『第一个旧字符串会被新字符串替代』
${变量//旧字符串/新字符串} 若变量内容符合『旧字符串』则『全部的旧字符串会被新字符串替代』
索引及切片
[root@linux-server ~]
[root@linux-server ~]
678
[root@linux-server ~]
4567
[root@linux-server ~]
34567
[root@linux-server ~]
3456
脚本案例:
[root@localhost ~]
read -s -p "请输入您的手机号 " phone
echo
echo "你的手机号是 $phone"
echo "手机号后四位是 ${phone:7}"
[root@linux-server ~]
[root@linux-server ~]
15
[root@linux-server ~]
www.sina.com.cn
变量内容的删除
[root@newrain ~]
sina.com.cn
[root@newrain ~]
cn
[root@newrain ~]
www.sina.com
[root@newrain ~]
www
[root@newrain ~]
www.sina.com.cn
[root@newrain ~]
com.cn
[root@localhost ~]
脚本案例:
read -p "请输入你的邮箱 " mail
echo "你的邮箱是$mail"
echo "你的邮箱服务器是${mail#*@}"
mail_host=${mail
case $mail_host in
163.com)
echo "网易服务器"
;;
126.com)
echo "126服务器"
;;
qq.com)
echo "qq邮箱"
;;
*)
echo "您输入的邮箱不正确"
exit 2
esac
参数解释:
*:表示全部字符。
%:最短尾匹配;
%%:最大尾匹配
%:从右往左
用冒号截取:echo $a: : :
从哪里截取留那里。
变量内容的替换
[root@linux-server ~]
[root@linux-server ~]
23456123789
[root@linux-server ~]
023456123789
[root@linux-server ~]
2345623789
[root@linux-server ~]
x23456x23789
例:
file=/dir1/dir2/dir3/my.file.txt
${file
${file
${file
${file
${file%/*}: 拿掉最后条 / 及其右边的字符串:/dir1/dir2/dir3
${file%%/*}: 拿掉第一条 / 及其右边的字符串:(空值)
${file%.*}: 拿掉最后一个 . 及其右边的字符串:/dir1/dir2/dir3/my.file
${file%%.*}: 拿掉第一个 . 及其右边的字符串:/dir1/dir2/dir3/my
记忆的方法为:
% 是去掉右边(在键盘上 % 在 $ 之右边)
单一符号是最小匹配;两个符号是最大匹配(贪婪匹配)。
basename & dirname
basename 命令
basename 是去除目录后剩下的名字,取文件名
例:
[root@linux-server ~]
[root@linux-server ~]
[root@linux-server ~]
1.test
dirname 是取目录名
例:
[root@linux-server ~]
[root@linux-server ~]
[root@linux-server ~]
/home/temp