1、基础概念
1.1 程序组成
程序:算法 + 数据结构
数据:是程序的核心
算法:处理数据的方式
数据结构:数据在计算机中的类型和组织方式
1.2 编程分格
过程式:以指令为中心,数据服务于指令
对象式:以数据为中心,指令服务数据
1.3 编程语言
计算机:运行二进制指令
编程语言:人与计算机之间交互的语言,分低级语言和高级语言
低级:汇编
高级:
编译:高级语言 → 编译器 → 目标代码(java,C),管理员要编译
解释:高级语言 → 解释器 → 机器代码(shell,perl,python),底层自动翻译
2、shell 程序
shell 编程:过程式、解释执行
shell 编程语言的基本结构:
各种系统命令的结合
数据存储:变量、数组
表达式:a+b(例)
语句:if(例)
2.1 shell 脚本基本格式
(1)首行 shebang 机制
#!/bin/bash #!/usr/bin/python #!/usr/bin/perl
(2)脚本规范
第一行一般为调用使用的语言(shebang 机制)
紧接着是注释说明,一般包含如下信息:
程序名,避免更改文件名而无法找到正确的文件;
版本号;
更改时间;
作者相关信息;
该程序的作用,及注意事项;
最后是各版本的更新简要说明。
#例: #!/bin/bash # #********************************************* #Author: #Blog: #Time:2020-05-24 11:53:58 #Name:hello.sh #Version:V1.0 #Description:This is a test script. #*********************************************
2.2 shell 脚本的用途
自动化常用命令;
执行系统管理和故障排除;
创建简单的应用程序;
处理文本或文件等。
2.3 创建、执行脚本的过程
(1)使用文本编辑器来创建文本文件
首行 shebang 机制;
注释说明信息。
(2)运行脚本
方法一:赋予脚本执行权限,指定绝对或相对路径执行脚本;
./hello.sh #相对路径 /data/shell/hello.sh #绝对路径
方法二:赋予脚本执行权限,加入 PATH 变量路径,直接运行脚本;
chmod +x /data/shell/hello.sh vim /etc/profile export PATH=/data/shell/:$PATH hello.sh #执行
方法三:直接运行解释器,将脚本作为解释器程序的参数运行。
bash test.sh cat test.sh | bash
方法四:搭建脚本服务器,存放脚本文件,需要使用时,远程拉取运行解释器执行。
curl http://10.0.0.3/hello.sh | bash
2.4 脚本测试
bash -n test.sh # 检查语法是否错误 bash -x test.sh # 跟踪调试执行
2.5 变量
字符串在内存中存存储以二进制进行存储、寻找,而我们使用变量名来访问该字符串。也可以重新定义字符串,重新画一块地址空间存放新字符串,指针指向新字符串的内存空间,原来的内存空间也在但是可以回收了。
(1)变量的作用
1> 数据存储格式;
2> 参与的运算;
3> 表示的数据范围。
(2)变量的类型
字符:shell 不分数据类型,都认为是字符串
数值:整型、浮点型
强类型:变量不经过强制转换,它永远是这个数据类型,不允许隐式的类型转换。一般定义变量时必须指定类型、参与运算必须符合类型要求,调用未声明变量会产生错误。
如 java,c
弱类型:语言的运行时会隐式做数据类型转换。无须指定类型,默认均为字符型,参与运算会自动进行隐式类型转换,变量无须事先定义可直接调用。
如:bash 不支持浮点数,php
(3)变量命名法则
1> 不能使用程序中的保留字:例如 if,for
2> 只能使用数字、字母及下划线,且不能以数字开头
3> 见名知意
4> 统一命名规则:驼峰命名法
[root@centos7 ~]# NAME="zhangsan" # 定义变量 [root@centos7 ~]# echo $NAME # 显示变量 zhangsan [root@centos7 ~]# HOSTNAME=`hostname` [root@centos7 ~]# echo $HOSTNAME centos7.localdomain [root@centos7 ~]# set # 显示所有变量 [root@centos7 ~]# unset HOSTNAME # 删除变量
(4)set 定制 shell 环境
[root@centos7 ~]# echo $- himBH h:开启hash i:交互式 m:监控模式(进程前后端切换) B:{} H:历史命令 [root@centos7 ~]# set +h # 禁用hash [root@centos7 ~]# hash -bash: hash: 哈希已禁用 [root@centos7 ~]# set -h # 启用hash # 脚本执行建议设置: set -uex u:在扩展一个没有设置的变量时,显示错误信息 e:脚本中当遇到错误,后续将不执行 x:相当于bash -x
(5)bash 中变量的种类
局部变量:生效范围为当前 shell 进程
[root@centos7 ~]# NAME="zhangyy" [root@centos7 ~]# echo $NAME zhangyy
环境(全局变量):生效范围为当前 shell 进程及其子进程
#定义环境变量:export 或 declare -x [root@centos7 ~]# export NAME="zhangsan" [root@centos7 ~]# echo $NAME zhangsan [root@centos7 ~]# echo $BASHPID 8196 [root@centos7 ~]# bash # 开启子进程 [root@centos7 ~]# echo $NAME zhangsan [root@centos7 ~]# echo $BASHPID 28738 [root@centos7 ~]# exit exit #显示所有的环境变量:env
本地变量:生效范围为当前 shell 进程中某代码片断,通常指函数
位置变量:在脚本中调用通过命令行传递给脚本的参数(脚本后跟参数实现不同功能)
$1,$2,…:对应第 1、第2...参数 $0 :命令本身(针对同一程序创建不同软链接,$0 值不同则实现不同功能) $* :传递给脚本的所有参数,全部参数合为一个字符串 $@ :传递给脚本的所有参数,每个参数为独立字符串 $# :传递给脚本的参数个数 $@ $* : 只在被双引号包起来才会有差异(脚本调用的时候) set -- : 清空所有位置变量
特殊变量:$?,$$(当前进程的进程编号),$PPID(父进程的进程 ID)
只读变量:只能声明,但不能修改和删除
#声明只读变量: readonly PI=3.1415926 #显示所有只读变量: readonly 或 declare -r
2.6 bash 如何开展命令行
(1)把命令行分成单个命令;
(2)展开别名;
(3)展开大括号 {} 声明,例:echo {1..3}
(4)展开波浪符 ~ 声明,例:家目录
(5)命令替换 $() 或 ``,例:`hostname`
(6)再次把命令行分成命令;
(7)展开文件通配符;
(8)准备 I/O 重导向;
(9)运行命令。
2.7 第一个脚本
第一个脚本实现:拷贝脚本,拷贝本机文件至远程主机
[root@centos7 ~]# cat scp.sh #!/bin/bash # #********************************************* #Author:zhang #Blog: #Time:2020-05-26 18:01:31 #Name:scp.sh #Version:V1.0 #Description:This is a test script. #********************************************* echo "Start copy..." scp $* root@192.168.100.111:/data/ # $*: 指传递给脚本的参数 echo "End of copy."
[root@centos7 ~]# [root@centos7 ~]# bash scp.sh anaconda-ks.cfg Start copy... root@192.168.100.111's password: anaconda-ks.cfg 100% 1530 783.2KB/s 00:00 End of copy.
2.8 运算
(1)算数运算
+,-,*,/,%,**
实现算数运算:
方法一:let var=算术表达式
[root@centos7 ~]# let sum=1+2+3+4 [root@centos7 ~]# echo $sum 10
方法二:var=$[算术表达式]
[root@centos7 ~]# z=$[2*3] [root@centos7 ~]# echo $z 6
方法三:var=$((算术表达式))
[root@centos7 ~]# a=6;b=7;c=$((a+b)) [root@centos7 ~]# echo $c 13
方法四:var=$(expr arg1 arg2 arg3…)
[root@centos7 ~]# n=$(expr 6 * 7) expr: 语法错误 [root@centos7 ~]# n=$(expr 6 * 7) # *号得转义 [root@centos7 ~]# echo $n 42
方法五:echo ‘算术表达式’ | bc
[root@centos7 ~]# echo {1..100} | tr ' ' '+' | bc 5050
(2)逻辑运算
0: false,1: true
与: &,或: | 0&0=0 0|0=0 0&1=0 0|1=1 1&0=0 1|0=1 1&1=1 1|1=1
非: !
异或: ^ 0^1=1
0^0=0
1^0=1
1^1=0
短路与: &&,短路或: || 0&&0=0 0||0=0 0&&1=0 0||1=1 1&&0=0 1||0=1 1&&1=1 1||1=1
短路与应用:&&
cmd1 && cmd2 # 如果cmd1为真,执行cmd2;如果cmd1为假,不执行cmd2
短路或应用:||
cmd1 || cmd2 # 如果cmd1为假,执行cmd2;如果cmd1为真,不执行cmd2
#异或^实例: [root@centos7 ~]# a=2 [root@centos7 ~]# b=3 [root@centos7 ~]# let c=a^b [root@centos7 ~]#echo $c 1 # 2=0010 # 3=0011 # ^——————— # c=0001 [root@centos7 ~]# echo $[a^c] 3 #异或实现ab值互换: [root@centos7 ~]#a=4 [root@centos7 ~]#b=5 [root@centos7 ~]#a=$[a^b];b=$[a^b];a=$[a^b];echo $a $b 5 4
#逻辑与&&实例: [root@centos7 ~]# ping -c1 -W2 www.baidu.com &> /dev/null && echo "www.baidu.com network reachable"
www.baidu.com network reachable #逻辑或||实例: [root@centos7 ~]# id yang uid=1000(yang) gid=1000(yang) 组=1000(yang) [root@centos7 ~]# id mimi id: mimi: no such user [root@centos7 ~]# id yang &> /dev/null || useradd yang # 如果yang用户不存在则创建用户 [root@centos7 ~]# id mimi &> /dev/null || useradd mimi # 如果mimi用户不存在则创建用户 [root@centos7 ~]# id mimi uid=1005(mimi) gid=1005(mimi) 组=1005(mimi)
2.9 条件测试
判断某需求是否满足,需要有测试机制来实现,专用的测试表达式需要由测试命令辅助完成测试过程。
评估布尔声明,以便用于在条件性执行中:
若真,则返回 0
若假,则返回 1
测试命令:
test EXPRESSION
[ EXPRESSION ]
[[ EXPRESSION ]]
注意:EXPRESSION 前后必须有空白字
(1)数字测试
-v VAR: 判断变量 VAR 是否设置 -eq : 等于 -ne : 不等 -lt : 小于 -le : 小于等于 -gt : 大于 -ge : 大于等于 #注: 比较的时候,如果变量没有值,会报错所以在[ ]内要加上"" :专门返回正确什么也不干 false 专门返回错误结果什么也不干
(2)字符串测试
= : 是否等于 > : 是否大于(ascii 码) < : 是否小于 != : 是否不等于 == : 右边支持通配符 =~ : 右侧支持扩展正则 -z "STRING" : 字符串是否为空,空为真,不空为假 -n "STRING" : 字符串是否为不空,不空为真,空为假
[root@centos7 ~]# n=34 [root@centos7 ~]# [[ "$n" =~ ^[[:digit:]]+$ ]] && echo "digit" || echo "no digit" digit [root@centos7 ~]# ll scp.sh -rwxr-xr-x 1 root root 339 5月 26 18:06 scp.sh [root@centos7 ~]# filename=scp.sh [root@centos7 ~]# [[ $filename =~ .+.sh$ ]] && echo sh || echo no sh sh [root@centos7 ~]# filename=anaconda-ks.cfg [root@centos7 ~]# [[ $filename =~ .+.sh$ ]] && echo sh || echo no sh no sh
[root@centos7 ~]# IP=1.2.3.4; [[ "$IP" =~ ^([0-9]{1,3}.){3}[0-9]{1,3}$ ]]; echo $?
0
[root@centos7 ~]# IP=1.2.3.4.5; [[ "$IP" =~ ^([0-9]{1,3}.){3}[0-9]{1,3}$ ]]; echo $?
1
# 判断空字符串
[ -z $var ]
[ ! -n $var ]
[ "$var"="" ]
[ x"var"="x" ]
(3)文件测试
#文件存在性测试: -a FILE:同-e -e FILE:文件存在性测试,存在为真,否则为假 #文件存在性及类别测试: -b FILE:是否存在且为块设备文件 -c FILE:是否存在且为字符设备文件 -d FILE:判断是否为目录文件 -f FILE:是否存在且为普通文件 -h FILE 或-L FILE:是否存在且为符号链接文件 -p FILE:是否存在且为命令管道文件 -S FILE:是否存在且为套接字文件 #文件权限测试: -r FILE:是否存在且可读 -w FILE:是否存在且可写 -x FILE:是否存在且可执行 -u FILE:是否存在且拥有 suid 权限 -g FILE:是否存在且拥有 sgid 权限 -k FILE:是否存在且拥有 sticky 权限 #文件属性测试: 文件大小测试: -s FILE:是否存在且非空 文件是否打开: -t fd:fd 文件描述符是否在某终端已经打开 -N FILE:文件自从上一次被读取之后是否别修改过 -O FILE:当前有效用户是否为文件属主 -G FILE:当前有效用户是否为文件属组 #文件双目测试: FILE1 -ef FILE2:FILE1 是否是 FILE2 的硬链接 FILE1 -nt FILE2:FILE1 是否新于 FILE2(mtime) FILE1 -ot FILE2:FILE1 是否旧于 FILE2 #help test 查看更多(test 是内置命令)
[root@centos7 ~]# [ -d /etc ]; echo $? 0 [root@centos7 ~]# [ -d /etc/issue ]; echo $? 1 [root@centos7 ~]# ll /etc/shadow ---------- 1 root root 806 5月 25 15:55 /etc/shadow [root@centos7 ~]# [ -x /etc/shadow ]; echo $? 1
(4)组合测试
第一种方式: COMMAND1 && COMMAND2 并且 COMMAND1 || COMMAND2 或者 第二种方式: EXPRESSION1 -a EXPRESSION2 并且 EXPRESSION1 -o EXPRESSION2 或者 ! EXPRESSION
[root@centos7 ~]# ll anaconda-ks.cfg -rw------- 1 root root 1530 4月 16 14:36 anaconda-ks.cfg [root@centos7 ~]# FILE=anaconda-ks.cfg [root@centos7 ~]# [ -f $FILE -a -x $FILE ]; echo $? 1 [root@centos7 ~]# [ -f $FILE -o -x $FILE ]; echo $? 0 [root@centos7 ~]# [ ! -x $FILE ]; echo $? 0
[root@centos7 ~]# [ -f $FILE ] && [[ "$FILE" =~ .*.sh$ ]] && chmod +x $FILE
13.5 ()小括号开启子进程
[root@centos7 ~]# name=zhang;(echo $name;name=yang;echo $name);echo $name zhang yang zhang
14、使用 read 命令来接受输入
read [-option] [name] -p:指定要显示的提示 -s:静默输入,一般用于密码 -n N:指定输入的字符串长度 N -d:’字符’ 输入结束符 -t N:TIMEOUT 为 N 秒 注:read 从标准输入中读取值,给每个单词非配一个变量,所有剩余单词都被分配给最后一个变量
[root@centos7 ~]# read -p "please enter your name:" name please enter your name:yang [root@centos7 ~]# echo $name yang [root@centos7 ~]# read var1 var2 var3 #建议一次定义一个值 1 2 3 [root@centos7 ~]# echo $var1 $var2 $var3 1 2 3
[root@centos7 ~]# echo zhangyy | read NAME #开启子进程
[root@centos7 ~]# echo $NAME
[root@centos7 ~]# echo zhangyy | { read NAME; echo $NAME; }
zhangyy
15、bash shell 配置文件
/etc/profile #全局配置
~/.bashrc #个人配置
15.1 交互式登录:
(1)直接通过终端输入账号密码登录
(2)使用 "su - UserName" 切换的用户
配置文件执行顺序: /etc/profile --> /etc/profile.d/*.sh --> ~/.bash_profile --> ~/.bashrc --> /etc/bashrc
15.2 非交互式登录:
(1)su UserName
(2)图形界面下打开的终端
(3)执行脚本
(4)任何其它的 bash 实例
配置文件执行顺序: /etc/profi1e.d/*.sh --> /etc/bashrc --> ~/.bashrc
15.3 按功能划分分类:
(1)profile 类:
profile 类为交互式登录的 shell 提供配置
全局:/etc/profile, /etc/profile.d/*.sh
个人:~/.bash_profile
功用:
1.用于定义环境变星
2.运行命令或脚本
(2)bashrc 类:
bashrc 类为非交互式和交互式登录的 shell 提供配置
全局:/etc/bashrc
个人:~/.bashrc
功用:
1.定义命令别名和函数
2.定义本地变量
15.4 编辑配置文件生效:
修改profile和bashrc文件后需生效两种方法:
1.重新启动shell进程
2.shurce|. 配置文件
15.5 bash退出任务:
保存在 .bash_logout 文件中
功能:自动备份、清理临时文件
16、流程控制
16.1 单分支:
#语法:
if 判断条件;then 条件为真的分支代码 fi
16.2 双分支:
#语法:
if 判断条件;then 条件为真的分支代码 else 条件为假的分支代码 fi
16.3 多分支:
#语法:
if 判断条件1;then 条件1为真的分支代码 elif 判断条件2;then 条件2为真的分支代码 elif 判断条件3;then 条件3为真的分支代码 else 以上条件为假的分支代码 fi
#实例:BMI指数 [root@centos7 ~]# cat if_bmi.sh #!/bin/bash # #********************************************* #Author:zhang #Blog: #Time:2020-05-26 13:53:00 #Name:if_bmi.sh #Version:V1.0 #Description:This is a test script. #********************************************* read -p "Please input your height:" HEIGHT if [[ ! $HEIGHT =~ ^[0-2].?[0-9]{,2}$ ]];then echo "输入错误的身高";exit 1;fi read -p "Please input your weight:" WEIGHT if [[ ! $WEIGHT =~ ^[0-9]{1,3}$ ]];then echo "输入错误的体重";exit 1;fi BMI=`echo $WEIGHT/$HEIGHT^2|bc` if [ $BMI -le 18 ];then echo "太瘦了,多吃点" elif [ $BMI -lt 24 ];then echo "身材很棒" else echo "肥胖" fi [root@centos7 ~]# bash if_bmi.sh Please input your height:1.8 Please input your weight:60 太瘦了,多吃点
16.4 条件判断 case 语句,适合通配符的匹配
#语法:
case 变量引用 in PAT1) 分支1 ;; PAT2) 分支2 ;; ... *) 默认分支 ;; esac*
#实例: [root@centos7 ~]# cat case_yesno.sh #!/bin/bash # #********************************************* #Author:zhang #Blog: #Time:2020-05-26 14:18:33 #Name:case_yesno.sh #Version:V1.0 #Description:This is a test script. #********************************************* read -p "Whether to install(yes/no)?" INSTALL INSTALL=`echo $INSTALL | tr 'A-Z' 'a-z'` case $INSTALL in y|yes) echo "install." ;; n|no) echo "not install!" ;; *) echo "input error!please input yes or no!" esac [root@centos7 ~]# bash case_yesno.sh Whether to install(yes/no)?y install. [root@centos7 ~]# bash case_yesno.sh Whether to install(yes/no)?no not install!
17、循环控制
17.1 for 循环
#语法1:
for 变量名 in 列表;do 循环体 done
#实例: [root@centos7 ~]# for i in {1..10};do echo $i;done 1 2 3 4 5 6 7 8 9 10 [root@centos7 ~]# for USER in `getent passwd | cut -d: -f1`;do echo username:$USER;done username:root username:bin username:daemon username:adm username:lp ......
#脚本实例:批量创建用户 [root@centos7 ~]# cat for_user.sh #!/bin/bash # #********************************************* #Author:zhang #Blog: #Time:2020-05-26 14:45:56 #Name:for_user.sh #Version:V1.0 #Description:This is a test script. #********************************************* USER=" xiaozhang xiaoyang xiaomu xiaoli " for NAME in $USER;do useradd $NAME echo 111111 | passwd --stdin $NAME &> /dev/null echo "$NAME created successfully." done
#脚本实例:99乘法表 [root@centos7 ~]# cat for_99.sh #!/bin/bash # #********************************************* #Author:zhang #Blog: #Time:2020-05-26 16:25:20 #Name:for_99.sh #Version:V1.0 #Description:This is a test script. #********************************************* for i in {1..9};do for j in `seq $i`;do x=$[i*j] echo -e "${j}X${i}=${x} c" done echo done [root@centos7 ~]# bash for_99.sh 1X1=1 1X2=2 2X2=4 1X3=3 2X3=6 3X3=9 1X4=4 2X4=8 3X4=12 4X4=16 1X5=5 2X5=10 3X5=15 4X5=20 5X5=25 1X6=6 2X6=12 3X6=18 4X6=24 5X6=30 6X6=36 1X7=7 2X7=14 3X7=21 4X7=28 5X7=35 6X7=42 7X7=49 1X8=8 2X8=16 3X8=24 4X8=32 5X8=40 6X8=48 7X8=56 8X8=64 1X9=9 2X9=18 3X9=27 4X9=36 5X9=45 6X9=54 7X9=63 8X9=72 9X9=81
#实例实现:1+2+···+100 [root@centos7 ~]# cat for_sum.sh #!/bin/bashsum=0 for i in {1..100};do let sum+=i done echo $sum
#语法2: for (( exp1; exp2; exp3 )); do COMMANDS; done Frithmetic for loop. #执行流程: 1.执行exp1 2.执行exp2(条件判断) 3.exp2成功,执行COMMANDS;exp2失败,退出循环 4.执行完COMMANDS,执行exp3 5.exp3执行完返回到exp2继续判断
#实例实现:1+2+···+100
[root@centos7 ~]# cat for_sum2.sh #!/bin/bashfor (( sum=0,i=1;i<=100;i++ ));do let sum+=i done echo $sum
#实例实现:扫描网段可访问主机ip [root@centos7 ~]# cat for_scanip.sh net=172.16.1 for (( i=1;i<=254;i++ ));do ping -c1 -W1 $net.$i &> /dev/null && { echo $net.$i is up;echo $net.$i >> host.log; } done
17.2 while 循环
格式: while CONDITION;do 循环体 done
#说明: #CONDITION:循环控制条件
进入条件:CONDITION为true 退出条件:CONDITION为false
#实例实现,磁盘空间报警邮件发送(每一分钟检查一次) [root@centos7 ~]# yum install mailx -y [root@centos7 ~]# cat .mailrc set from=1611748256@qq.com # 发件人 set smtp=smtp.qq.com set smtp-auth-user=1611748256@qq.com set smtp-auth-password=dcrxotejwmiheexxxx # 授权码 set smtp-auth=login set ssl-verify=ignore [root@centos7 ~]# cat while_diskcheck.sh #!/bin/bash WARNING=80 while :;do USE=`df|sed -rn '/^/dev/sd/s#.* ([0-9]+)%.*#1#p'|sort -nr|head -n1` if [ $USE -gt $WARNING ];then echo Disk will be full from `hostname -I`| mail -s "disk warning" zyyyxdz@163.com fi sleep 60 done
#screen命令或tmux命令,实现退出窗口不中断
#菜单实现 [root@centos7 bin]# cat menu.sh #!/bin/bash COLOR="