shell总览
1、初始shell
计算器语言的类型、定义、分类,使用场景、常见元字符
计算器语言的类型
编译型语言 c语言、c++语言、java语言 运行编译型语言是相对于解释型语言存在的,编译型语言的首先将源代码编译生成机器语言,再由机器运行机 只翻译第一次,后面则只需要使用第一次翻译的内容 解释型语言 php语言、python语言、bash语言 相对于编译型语言存在的,源代码不是直接翻译成机器语言,而是先翻译成中间代码,再由解释器对中间代码 进行解释运行。 每次都得翻译,不会保存
shell的定义
一种程序语言,有变量、关键字、各种控制语句,有自己的语法结构,利用shell程序设计的语言,可以编写功能很强、代码阶段的程序 #! shebang 定义解释器。如#!/bin/bash,使用/bin/bash执行脚本(bash shell)会解释该文本下的内容,执行时,bash会解释好显示出来
shell的分类
/bin/sh 符号连接,/bin链接的是use/bin, /bin/bash bash shell的标准安装位置 /sbin/nologin 阻止用户登陆的系统 echo $SHELL 查看当前正在使用的shell vim /etc/passwd 编辑登录时使用的shell
使用场景
什么时候不适合使用Shell编程: 1. 资源紧张的项目,特别是那些速度是重要因素的地方(排序,散序,等等) 2. 程序要进行很复杂的数学计算,特别是浮点计算,任意精度的计算,或者是复数计算 3. 要求交叉编译平台的可移植性(使用C或者是Java代替) 4. 需要结构化编程的复杂应用(需要变量类型检查和函数原型等等) 5. 对于影响系统全局性的关键任务应用。 6. 安全非常重要。你必须保证系统完整性和抵抗入侵,攻击和恶意破坏。 7. 项目由连串的依赖的各个部分组成。 8. 多种文件操作要求(Bash被限制成文件顺序存取,并且是以相当笨拙,效率低下的逐行的存取方式) 9. 需要良好的多维数组支持。 10. 需要类似链表或树这样的数据结构。 11. 需要产生或操作图象或图形用户界面。 12. 需要直接存取系统硬件。 13. 需要端口号或是socket I/O。 14. 需要使用可重用的函数库或接口。 15. 所有的私有的不开源的应用程序(Shell脚本的源代码是直接可读,能被所有人看到的)
Shell 能做什么? 1. 自动化批量系统初始化程序 (update,软件安装,时区设置,安全策略...) 2. 自动化批量软件部署程序 (LAMP,LNMP,Tomcat,LVS,Nginx) 3. 应用管理程序 (KVM,集群管理扩容,MySQL,DELLR720批量RAID) 4. 日志分析处理程序(PV, UV, 200, !200, top 100, grep/awk) 5. 自动化备份恢复程序(MySQL完全备份/增量 + Crond) 6. 自动化管理程序(批量远程修改密码,软件升级,配置更新) 7. 自动化信息采集及监控程序(收集系统/应用状态信息,CPU,Mem,Disk,Net,TCP Status,Apache,MySQL) 8. 配合Zabbix信息采集(收集系统/应用状态信息, CPU,Mem,Disk,Net,TCP Status,Apache,MySQL) 8. 自动化扩容(增加云主机——>业务上线) zabbix监控CPU 80%+|-50% Python API AWS/EC2(增加/删除云主机) + Shell Script(业务 上 线) 9. 俄罗斯方块,打印三角形,打印圣诞树,打印五角星,运行小火车,坦克大战,排序实现 10. Shell可以做任何运维的事情(一切取决于业务需求)
shell的常见元素
文件描述符与输出重定向 0:Standard Input (STDIN) 标准输入 1:Standard Output (STDOUT) 标准正确输出 2:Standard Error Output (STDERR) 标准错误输出 >a 将正确输出的内容,输入到文本a 2>a 将错误输出的内容,输入到文本a &>a 将正确错误输出的内容,混合输入到文本a 1>&2 将正确的输出当错误输出用 2>&1 将错误的输出当正确输出用
bash配置文件、bash shell特性、bash快捷键
bash配置文件
开局加载的一些乱七八糟的图案文件 /etc/motd 全局配置文件 /etc/profile /etc/profile.d/*.sh /etc/bashrc 个人配置文件 /root/.bash_profile /root/.bashrc profile 在用户登陆时执行 bashrc 在bash shell被打开时执行,每次开启新的终端或标签页时,都会读取这个文件 用户登录加载bash配置文件的顺序 登陆式shell加载配置文件过程 1、/root/.bash_profile 2、/root/.bashrc 3、/etc/bashrc 4、/etc/profile 5、/etc/profile.d/*.sh 非登录式shell加载文件过程 1、/root/.bashrc 2、/etc/bashrc 3、/etc/profile.d/*.sh
bash shell特性
历史 history 别名 alias 快捷键 tab 前后台作业 ctrl+z 将前台正在运行的作业放到后台,jobs 查看所有后台正在运行的作业, fg %1 将后台正在运行的作业,转移到前台,bg %1 将后台暂停的作业,重新启动在后台运行 重定向 > 管道 | 命令排序执行: ; 命令分割符 && 前面成功,执行后面 || 前面不成功,执行后面 {} ?* 正则表达式
bash快捷键
ctrl+a 切换到命令开始行 ctrl+e 切换到命令行末尾 ctrl+u 清除剪切光标之前的内容 ctrl+k 清除剪切光标之后的内容 ctrl+y 粘贴刚才所删除的字符(恢复u、k删除的内容) ctrl+r 在历史命令中查找,输入关键字调出之前的命令
历史命令、通配符置换
历史命令
查看历史命令 history 查看历史命令 /etc/profile下的historysize可以修改 显示历史命令的执行时间,添加变量,再使用history则会有时间显示 HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S" 调用历史命令 1、上下键 2、! 关键字 3、! 历史命令行号 4、!! 执行上一条命令 5、!$ 上一条命令的最后一个参数 esc . 上一条命令的最后一个参数 ctrl+r 在历史命令中查找,输入关键字调出之前的命令 别名 查看别名 alias 设置别名 临时设置 a=88 重启后失效 永久设置 vim /root/.bashrc 添加别名
通配符置换
* 匹配0或多个字符 例如:a*b,a开头b结尾,不管中间有多少字符,都会被算在内 ? 匹配一个字符 例如:a?b,a开头b结尾,中间只能有一个字符,可以是任意字符 [] 匹配[]中的某一个字符 例如:a[xyz]b,a开头b结尾,a与b中间只能有xyz其中一个字符才会匹配 a[0-9]b,a与b之间只能有一个a数字 a[!0-9]b,a与b之间只能有一个字符,但不能是阿拉伯数字 {} 匹配{}中的一串字符 例如:a{abc,xyz,123}b,a与b之间只能是abc、xyz、123这三个字符串之一
2、shell脚本规范
文件以.sh结尾,代表这个是shell脚本 .sh结尾的文件有高亮显示,省略sh则不易判断该文件是否为shell脚本 脚本规范如下 ====================================== #!/usr/bin/bash ---shebang蛇棒, 解释器, 翻译 # #Author: newrain ---Author 作者 #Email: newrain@163.com ---Email 邮箱 #Github: https://github.com/newrain001 ---该脚本的网址 #Date: 2019/**/** ---创建时间 echo "woaixuexi" ---打印我爱学习到终端屏幕 ====================================== 第一行#!/usr/bin/bash 语法规定shell脚本文件的第一行为为整个文件的解释器 第二行#开头的行为为注释器,用来说明文件及标定所属人使用,解释这个脚本的 执行脚本的方式 1、sh 脚本.sh 2、bash 脚本.sh 3、./脚本.sh 需要执行权限(chmod a+x)可执行所有脚本 4、. 脚本.sh 5、source 脚本.sh
3、变量
变量的类型、变量运算、变量引用、读取用户标准输入
变量的类型
变量 bash作为程序设计语言和其他高级语言一样,也提供使用和定义变量的功能 预定义变量、环境变量、自定义变量、位置变量 预定义变量: $? echo $? 最后一次执行命令的返回状态,变量为0时,则证明上一条命令正确;变量不为0,则证明上一条命令执行错误 $$ 执行命令时,最后加上$$,顺便生成的进程号(pid) $! echo $! 查看后台运行最后一个进程的进程号(pid) 自定义变量: 定义:变量名称=值 变量名称:只能由字母、数字、下划线组成;不能以数字开头 注意:要让变量名称有意义 = 赋值符号,前后不能有空格 引用变量:$变量名 或 ${变量名} 例如: a=100 添加变量a=100 echo $a 查看变量a等于100 echo $aa 如果输入两个aa,则查询不到aa变量,显示为空 echo %{a}a 将a用${}包起来,则a单独成为变量,与后面的a没有关系 查看单个变量:echo $变量名 查看所有变量:set 自定义变量和环境变量 取消变量:unset 变量名 环境变量: echo $变量名 查看单个变量 set 查看所有自定义变量和环境变量 enc 仅查看所有环境变量 unset 变量名 取消变量名 作用范围:仅在当前shell中生效 export 变量名称=值 设置环境变量,使该变量可以在子shell中使用 可以在子shell中使用 临时生效: a=1 重启后失效 永久生效: 写入到登录脚本中 /root/.bashrc /root/profile /etc/profile.d/* echo $PATH 存储所有命令所在的路径 预定义变量: (加在脚本中) $$ 当前进程pid $? 命令执行后的返回状态,0或非0 $# 位置参数的数量 $* 所有位数参数的内容 $@ 所有参数 $! 查看最后一个后台进程的pid 位置变量: vim s.sh =================== echo $1 添加预定义变量1 echo $2 添加预定义变量2 echo $@ 展示变量都有什么 echo $* 展示变量都有什么 echo $# 展示有几个参数 echo $$ 展示当前进程的pid echo $? 命令执行后的返回值 =================== sh s.sh x y 添加$1和$2的变量为x和y 变量嵌套(扩展) 表示(变量)$var的长度 # eval 执行字符串内的可执行命令 [root@newrain ~]# a=b [root@newrain ~]# b=c [root@newrain ~]# eval echo '$'"${a}" c 先获取a的值,通过再次构造echo命令,使用eval再一次执行语句,就达到我们的目的。
变量运算
变量运算: 算式运算符:+、-、*、/、()、%(取余数) 运算方式:$(())、$[]、expr 例如: $(())方式 echo $(( 5+2-(3*2)/5 )) =6 $[]方式 echo $[ 5 + 2 - (3*2)/5 ] (3*2)/5是6/5,这里是整数除法,忽略余数,所以等于1 =6 expr方式(适合简单的计算,加减乘除不好混合用),因为*、/在shell中有特殊的含义,所以要加\用来转义 expr 5 +3 =8 expr 5 \* 8 =40 expr 5'*' 8 =40 浮点运算: bash本身不能做小数点计算,需要用bc命令转换 例如: echo "2*4" | bc 2乘4 echo "2^4" | bc 2的4次方 echo "scale=2;6/4" | bc scale 精度 小数点两位,6除以4等于1.5,精度为小数点两位,所以等于1.50 例子: 计算信用卡一年的利息,日利率为0.05% m=$( echo 5/10000|bc -1 ) m等于5除以10000的值,这里的等于0.0005,-l表示使用标准的数学库 或m=`echo 5/10000|bc -1` sum=10000 设置变量sum等于10000 for i in {1..365} 循环365次 do 开始循环 sum=$(echo $sum+$sum*$m | bc ) sum赋予新的值, echo &sum 每次循环迭代输出当前的sum值 done 循环结束 echo $sum 循环结束后,输出最终的sum值
bc命令详细解释
bc命令 是一种支持任意精度的交互执行的计算器语言。bash内置了对整数四则运算的支持,但是并不支持浮点运算,而bc命令可以很方便的进行浮点运算,当然整数运算也不再话下。 语法: bc(选项)(参数) 选项: -i:强制进入交互式模式; -l:定义使用的标准数学库; -w:对POSIX bc的扩展给出警告信息; -q:不打印正常的GNU bc环境信息; -v:显示指令版本信息; -h:显示指令的帮助信息。 参数: 文件:指定包含计算任务的文件。 实例: 算术操作高级运算bc命令它可以执行浮点运算和一些高级函数: echo "1.212*3" | bc 3.636 设定小数精度(数值范围) echo "scale=2;3/8" | bc 0.37 参数scale=2是将bc输出结果的小数位设置为2位。 进制转换 #!/bin/bash abc=192 echo "obase=2;$abc" | bc 执行结果为:11000000,这是用bc将十进制转换成二进制 #!/bin/bash abc=11000000 echo "obase=10;ibase=2;$abc" | bc 执行结果为:192,这是用bc将二进制转换为十进制。 计算平方和平方根: echo "10^10" | bc echo "sqrt(100)" | bc
变量引用
\ 转义符号 当一个字符被引用时,其特殊含义将被无效 将有意义的变为无意义,将无意义的变为有意义 例如:\n 输入:echo -e '5\\n6\n7' 输出:5\n6 7 \n是换行符,如果不加\,则换行,如果加\,则代表\n是一个无意义的符号 '' 完全引用、强引 "" 部分引用、弱引 例子: 设置变量;num=1 强引: 输入:echo '$num' 输出:$num 弱引: 输入:echo "$num" 输出:1
读取用户标准输入
read 读取键盘输入的值,赋予给变量 read -t 5 变量 等待用户输入的时间为五秒 read -p '提示信息' 变量 提供给用户应该输入的信息 read -s -p '提示信息' 变量 使read命令中输入的内容不显示在屏幕 read -n 5 -p "提示信息" 变量 -n用于指定读取的字符数,到五个字符直接返回结果,不用回车 stty -echo 取消屏幕回显,执行完该命令后,输入的所有东西将不会显示,只会显示输出的内容 stty echo 关闭取消屏幕回显
4、脚本运行
脚本大纲、脚本执行的方式
脚本大纲
1、创建脚本文件 指定命令解释器 注释 编写bash指令集合(脚本内容) 2、使用./执行,需要添加权限
脚本执行
在子shell中运行脚本: sh 脚本 bash 脚本 在子shell中执行脚本,并将执行环境带如到父shell . 脚本 source 脚本 所有程序都可以使用这种方式执行shell chmod a+x 添加权限 ./ 脚本 主要用sh sh -x 执行该脚本并显示所有的变量 sh -n 不执行脚本,只是检查语法错误,返回所有错误语法(不适用) sh -v 执行脚本前,把执行脚本内容显示在屏幕
5、变量替换
命令替换、变量替换、变量替换-匹配截取
命令替换
a=`date` 将a设置为date的变量 a=$(date) 和上面一样 echo $a 设置完命令变量之后,echo $a就和输入date是一样的效果
变量替换
1、${parameter:-word} 若parameter未设置,则用word代替parameter进行替换 =============== b=${b:-3} =============== 如果b有值,则b为1,如果b没有值,则b为3 2、${parameter:=word} 与前者类似 =============== b=${b:-3} =============== 如果b有值,则b为1,如果b没有值,则b为3 3、${parameter:+word} 若 parameter设置了,则用word代替parameter进行替换,parameter的值不变. =============== b=1 b=${b:+3} =============== 不管b有没有值,b的值都将成为3 4、${parameter:?message} 若parameter为空或未设置,则message作为标准错误打印出来,这可用来检查变量是否正确设置 =============== b=${b:?3} =============== 若变量以赋值的话,正常替换.否则将消息message送到标准错误输出(若此替换出现在Shell程序中,那么该程序将终止运行)
变量替换-匹配截取
变量内容删除及替换 # 是去掉左边(在键盘上 # 在 $ 之左边) % 是去掉右边(在键盘上 % 在 $ 之右边) 单一符号是最小匹配;两个符号是最大匹配(贪婪匹配)。 设置变量:url=www.sina.com.cn echo ${#url} 获取变量长度 echo ${url} 正常显示变量内容 ${变量#关键词} 从前往后,从符合第一个关键词的位置切除 输入:echo ${url#*.} 从前往后,最短匹配 输出:sina.com.cn ${变量##关键词} 从前往后,符合最后一个关键词的位置切除 输入:echo ${url##*.} 从前往后,最长匹配 输出:cn ${变量%关键词} 从后往前,从符合第一个关键词的位置切除 输入:echo ${url%.*} 从后往前,最短匹配 输出:www.sina.con ${变量%%关键词} 从后往前,符合最后一个关键词的位置切除 输入:echo ${url%%.*} 从后往前,最长匹配 输出:www ${变量/旧字符串/新字符串} 将旧字符串替换为新字符串,只替换第一个 输入:echo ${url/n/p} 匹配到第一个的旧字符替换为新字符 输出:www.sipa.com.cn ${变量//就字符串/新字符串} 替换所有旧字符串为新字符串 输入:echo ${url//n/u} 输出:www.sipa.com.cp 匹配到所有所有旧字符替换为新字符 =================================================================== 索引及切片 设置变量:a=12345678 输入:echo ${a:5} 从第五位的下一位开始截取 输出:678 输入:echo ${a:3:4} 从第三位的下一位开始截取,只截取四位 输出:4567 输入:echo ${a:2:-1} 从第二位的下一位开始截取,截取到倒数第一位的上一位 输出:34567
basename & dirname获取文件或目录名
basename 除去目录后剩下的文件名 temp=/home/temp/1.test 设置变量 base=`basename $temp` 设置命令变量 echo $base 查看变量 结果为:1.test 显示结果 dirname 除去文件后剩下的目录名 temp=/home/temp/1.test 设置变量 dir=`dirname $temp` 设置命令变量 echo $dir 查看变量 结果为:/home/temp 显示结果 在脚本中添加 ehco $( cd $( dirname $0 ) ; pwd ) 显示脚本所在的目录
6、shell编程-流程控制
shell编程-条件结构、分支if语句、分支case语句
shell编程-条件结构
test 是一个内置的命令,用于评估表达式并返回真(返回值为0)或假(非零返回值)。test 命令经常与条件语句(如 if、while、until)一起使用来控制程序的流程。 字符串比较、数字比较、文件测试 字符串 -n test -n "" ""中字符串长度不为空,则返回值为0,如果为空,则返回值为1 -z test -z "" ""字符串长度为零;不输入内容,则返回值为0,否则返回值为1 = test a = b 判断两个字符串是否一样,如果a=a,则返回值为0,否则返回值为1 != test a != b 判断两个字符串是否不一样,如果a=a,则返回值为1,否则返回值为0 ! -n = -z ! -z = -n 反转 数字 eq 等于 ne 不等于 ge 大于等于 le 小于等于 gt 大于 lt 小于 文件 -f 存在且是普通文件 -d 存在且是目录 -h 存在且是软链接 -b 块设备 -c 字符设备 -e 文件存在
shell分支if语句
控制流: 在一个shell脚本中命令执行的顺序称作脚本的流 流控制命令:能让脚本的流根据条件而改变命令,称作为条件流控制命名 exit语句:退出程序的执行,并返回一个返回码,返回码为0正常退出,非0为非正常退出 例如:exit 0 if语句,if代码返回0为真,非0为假 条件判断: ========================= if [ $1 -eq 1 ];then echo '等于1' elif [ $1 -eq 2 ];then echo '等于2' else echo '既不等于1,也不等于2' fi ========================= 多条件联合 逻辑与(和) if [ $1 ] && [ $2 ] if [ $1 -a $2 ] if [[ $1 && $2 ]] 逻辑或 if [ $1 ] || [ $2 ] if [ $1 -o $2 ] if [[ $1 || $2 ]]
shell分支case语句
case语句,输入的变量来判断 ====================== case $1 in a) echo 1 ;; b) echo 2 ;; c) echo 3 ;; *) echo "输入错误" exit ;; esac ====================== if与case的区别:if是从上往下一条一条判断,case是直接去寻找目标,都可以用
7、shell编程-循环结构
shell循环-for语句、while语句、until语句
shell循环for语句
for i in {取值范围} 循环的内容可以是{1..100}{a..z}以及空格,换行,tab键隔开的内容 do 循环开始 循环体 命令参数,执行一些操作后面跟上$i done 循环结束 循环脚本: ============= for i in {1..100} do echo $i done ============= 脚本作用: 挨个打印1到100 ======================== for i in $(seq 1 100) do echo $i done ======================== 和for i in {1..100}执行结果一样 seq 是一个用于生成序列的实用程序。它通常用于生成一系列的数字,但也可以生成其他序列 ======================== for (( i=1;i <= 5;i++ )) do echo $i done ======================== 类似c语言循环方式,可以使用,我还是觉着for i in {}更好用
shell循环while语句
while循环条件为真的情况下会循环,只要条件为真,可以一直循环 while 条件 while 关键字 条件 [ $1 -lt 10 ] do 开始循环 循环体 命令参数,执行一些操作后面跟上$1 done 循环结束 例子 ===================== #!/bin/bash i=0 while [ $i -lt 100 ] do echo $i let i++ done ===================== 只要i小于100,就会一直循环下去,该例子中,i每打印一次,值就会+1,所以i等于99的时候,就会停止循环
区别
for和while的区别 vim a ---------- vim wget sl ---------- 如果是for循环 -------------------- for i in $(cat ./a) do echo $i done -------------------- 输出结果: vim wget sl 如果是while循环 -------------------- while read l do echo $l done <./a -------------------- 或 -------------------- cat ./a | while read l do echo $l done -------------------- 输出结果 vim wget sl for循环会将空格键,tab键,换行符的结果都以换行符展示出来 while循环不会修改格式,空格就是空格,tab就是tab : 占位符 for循环 -------------------- for i in {1..100} do : #占位符,不会报错,方便以后填写内容 done -------------------- while循环 -------------------- while : #这个:是真的,和1=1一样,无限循环 do : #这个:是假的,就是为了z done --------------------
shell循环until语句
until 条件 当条件表达式,为假进行循环,为真则停止循环 do 循环体 done 例子 ===================== i=0 until [ 2 -eq 1 ] do echo $i let i++ done ===================== 因为2不等于1,所以会一直循环下去 ===================== x=1 until [ $x -ge 10 ] do echo $x x=`expr $x + 1` done ===================== expr的作用是求值表达式,用来计算$x+1后的值 while可以用>、=、<号,但是until只能用字母-eq等来表示,还是统一用字母好
shell循环控制continue、break、exit、shift
shell循环控制continue、break、exit
continue、break、exit ======================= echo "我在西安很想你" for i in {1..10} do if [ $i -eq 5 ];then continue、break、exit fi echo "想你第$i次" done ======================= continue 跳过单次循环,跳过第五次接着从第六次开始循环 break 跳过所有循环,遇到第五次的时候,将不会循环下列的循环参数,执行下一个脚本 exit 循环到第五次的时候,直接退出脚本
shell循环控制shift
shift命令 每次往命令命令参数前移动某位(shift后面跟的数字,不跟默认是1) ===================== while [ $# -ne 0 ] do echo $1 shift done ===================== 运行该脚本,后面跟上参数,每个参数只会运行一次,如果是shift 2则每次会往前移一位,如果参数是两个,则会显示第一个,如果参数是四个,则会显示第一个和第三个,参数只能为shift后面跟的数字的倍数,否则会报错 ===================== function test (){ a=$1;shift 1 echo $a echo $2 } test 1 2 3 ===================== 输出结果: 1 3 a=1 在没有shift前,$1就是位置变量第一个 $2=3 在shift后,$2就是位置变量向前移一位,就是位置变量的第三个
8、shell 编程-函数
定义函数、取消函数、函数传参、调用函数、命名空间、返回值
定义函数、取消函数、函数传参
function 函数名 (){ 内容 } 或 函数名 (){ 内容 } 完成特定功能的代码片段 函数必须先定义才能使用 优点:避免重复的代码 定义函数 a(){ echo "woaixuexi" } 定义函数a为,打印woaixuexi到终端 取消函数 unset 函数名 函数传参 设定一个函数,在该函数中添加候选变量,后面引用函数时,再加上变量
调用函数
调用函数 本地脚本中,直接输入函数名字就可以调用函数 其他脚本中 1、需要加source 脚本的的文件名 2、输入脚本的定义函数,只需要调用一次文件名,就可以在新的脚本中一直调用,如果需要使用第二个不在同一个脚本中的函数,还需要再添加一次不同函数的脚本名 1、创建脚本 a.sh ===================== zzz(){ echo 这是一个函数 } ===================== 2、创建调用函数的脚本 b.sh ===================== source 绝对路径/a.sh zzz ===================== 3、使用调用的函数 输入:sh b.sh 输出:这是一个函数
命名空间、返回值
命名空间 用法:local 变量 在shell脚本中,函数外和函数内的变量时一致的,函数内外不能赋值同样名字的变量,但是local可以实现局部变量,在函数开始时,输入local,则函数内的变量就和函数外没有关系 返回值 return 用来修改函数返回值的,可以将错的改为0,正确的改为其他,返回值不能超过0-255 注!!! 不能将函数名写为特殊字符,如ping,就会使函数失败,亲身经历 return返回值脚本: =================== function networkCheck(){ ping -w3 -c3 www.5idu.com &>/dev/null if [ $? -eq 0 ];then echo "网络正常" return 0 else ping -w3 -c3 8.8.8.8 &>/dev/null if [ $? -eq 0 ];then echo "DNS无误,可以修改/etc/sysconfig/network-scripts/ifcfg-ens33 解决" return 1 else echo "网络异常,请联系管理员" return 2 fi fi } networkCheck result=$? echo "网络返回值 $result,如果网络异常,可以访问http://jiaming,寻找解决方案或拨打电话" =================== 没有使用命名空间的脚本 =================== a=1 function test (){ echo $a a=2 } test echo $a =================== 执行结果: 1 2 函数和脚本中的变量是相通的 =================== a=1 function test (){ local a echo $a a=2 } test echo $a =================== 执行结果: 1 local a,将变量a分到函数的环境中,所以第一个echo $a的值为空,第二个echo $a的值是以函数外面所定义的变量a进行取值 local,命令空间,将变量的值放到一个新的环境中 面试题 =================== a=li b=$a function test (){ local a=$1 b=$2;shift export c=$(eval echo $3) } test 10 20 30 $b echo $a ${b} $c =================== $a 值为10 local没有定义将哪个变量作为局部变量,local在此处不起任何作用,$1等于test后面跟的第一个参数,为10 $b 值为20 同上 $c 值为li 在给b赋值后,shift,将位置变量向前移一位,此时$3就变成了位置变量的第4个参数,又因为$b=$a,$a=li,eval echo $3,将变量嵌套的值打印出来,所以$b=$a=li,$c=$b,所以$c=li
9、shell编程-数组
定义数组、访问数组、遍历数组
定义数组
普通数组:只能用整数作为数组的索引 普通数组下标只能是数字 关联数组:可以使用字符串作为数组的索引 关联数组下标可以自己设置 一个变量可以定义多个值 普通数组定义: 定义:a=(1 2 3 4) 输入:echo ${a[0]} 输出:1 输入:echo ${a[1]} 输出:2 输入:echo ${a[2]} 输出:3 输入:echo ${a[3]} 输出:4 关联数组需要提前声明 输入:declare -A 变量名 输入:wowo=([name]=ljm [sex]=nan [age]=18) 输入:echo ${wowo[name]} 输出:ljm 输入:echo ${wowo[sex]} 输出:nan 输入:echo ${wowo[age]} 输出:18 关联数字定义变量前必须输入declare -A,否则变量只会输入最后一个值 需要修改值的话,只需要输入变量加下标直接修改 普通数组,使用空格隔开的每个元素 declare -a filelist 默认输入的,不需要手动输入 定义普通数组: filelist=("li" "jia" "ming") 下标或索引 0 1 2 关联数组,必须先声明再定义 declare -A user 关联数组前的声明,全部打印出来的关联数组是无序的 user={[name]=jiaming [age]=18 [job]=student [gender]=nan} echo ${user[name]} 输出:jiaming echo ${filelist[@]} 打印所有元素 echo ${filelist[*]} 打印所有元素 echo ${!filelist[@]} 打印所有元素的下标 echo ${#filelist[$]} 打印元素的个数 数组案例1 ------------------------------ #a 完整备份 b 增量备份 c 差异备份 rule=(a b b c b b c) datetime=$[`date +%u` -1] case ${rule[$datetime]} in a) echo "开始进行完整备份" ;; b) echo "开始进行增量备份" ;; c) echo "开始进行差异备份" ;; esac ------------------------------ 数组案例2 ------------------------------ # ++i 先加1后赋值,i++ 先赋值后加1 while read line do host[++i]=$line done </etc/passwd for i in ${!host[@]} do echo $i--${host[$i]} done ------------------------------ 数组案例3 ------------------------------ user=(李 佳 明) #0-2 n=$[$RANDOM % 3] #不超过3 echo "随机抽取用户为${user[$n]}" ------------------------------ 随机生成一个si密码 ------------------------------ #!/bin/bash n=$((RANDOM % 8999)) a=$((n+1000)) echo $a ------------------------------
访问数组
设置变量:a=(1 2 3) echo ${a[0]} 访问数组中的第一个值 echo ${a[@]} 访问数组中所有的值 echo ${#a[@]} 打印值的个数 echo ${!a[@]} 打印索引(下标)
遍历数组
========================== #!/bin/bash 解释器 while read a 读取/etc/passwd的文件 do 开始循环读取 passwd[i++]=$a 将读取的文件设置为数组,并且按照每一行的类型设置为数组 done </etc/passwd 结束循环,添加输入的文件。完成数组的设置 for i in ${!passwd[@]} 循环数组的下标,后面是指数组的下标 do 开始循环 echo "$i:${passwd[i]}" 打印自增键,打印自增键对应数组下标的内容 done 结束循环 ========================== 读取文件中的行,然后加到一个数组中并进行遍历 自己理解:就是将文本中的内容,以行的形式添加成数组
10、正则表达式RE
正则表达式基本元字符、拓展元字符
正则表达式基本元字符
^ ^love love开头的语句 $ love$ love结束的语句 . l..e l开头e结尾,中间包含两个字母的单词,一个点代表一个字母 * o* 匹配字母o零次到多次, .* o*t o*t表示匹配前面o零次到多次,以t结尾 [] r[abcde] 匹配r后面紧跟着是abcde的单词,[abcde]单拿出来用,匹配括号中任意一个单词 [-] [a-z][0-9] 同上 [^] [^love] 取反,匹配文本中除了love这四个单词以外的单词 \ \n、\t、\b 转义,\n代表换行,\t代表一个tab键,\b退格,往前删一位 \< \<r 只要r开头的单词,^是只要开头的语句,这个是单词 \> t\> t结尾的单词 \(\) \(\) 匹配后的标签,不好搞,复制一份/etc/passwd文件测试 :%s/^\(.*\)\(:x:.*:\)\(.*\)/\3\2\1/g 使passwd中的文件,第一个:前的数据;第一个:后,最后一个:前的数据;最后一个:后的数据。调整一下位置,第一个整体代表数字1,第二个整体代表数字2,第三个整体代表数字3。使最后一段的位置放到第一个,第一段的位置放到最后一段,效果呈现 开始:(root)(:x:0:0:root:/root:)(/bin/bash) 结束:(/bin/bash)(:x:0:0:root:/root:)(root) echo -e 转义,使带有特殊含义的字符变得有意义 \d \d代表数字,默认为一个 \d+代表匹配多个数字 \s \s代表空格,默认为一个 \s+代表匹配多个空格,sed中不支持/s \n \n代表换行 \t \t代表一个tab键 \b \b往前删一位 \a \a发出警告声
正则表达式拓展元字符
grep -E 转义,默认正则开了一些转义 = 等于 != 不等于 =~ 匹配 + [a-z]+oot 匹配前面的字符一次到多次,oot结尾的单词 ? o?t 匹配前面的字符零到一次,只匹配ot和t | a|b 匹配文件中a或b的单词 () r(o|a) 匹配r下一个字母是o或a的单词 x{m} o{1} 匹配一次o,可以有无数个o,因为o是按一个一个算的 x{m,} o{1,} 最少匹配1次o x{,m} o{,3} 最多匹配3次o x{m,n} o{1,3} 最少匹配一次最多匹配三次o
11、shell编程-grep
egrep
grep 支持基本正则 grep -E 支持正则表达式的拓展元字符(或egrep) greo -P 支持扩展正则,所有正则都支持,egrep搞不定的,用grep -P都可以搞定 grep -F 不支持任何正则或特殊符号,写的什么,就过滤什么(或fgrep) grep -v 反向过滤 grep -i 不区分大小写 grep -o 只取出匹配到的部分(过滤[0-9]+、[a-z]+、[A-Z]+,可以使过滤的内容是一个整体) grep -c 过滤出包含内容的列数 '' 使变量没有意义,强引 "" 使双引号中的内容是一个整体,变量可以使用 变量a=1 1、运用正则,判断需要[[]] 变量a=1 [[ $a =~ ^[0-9]+$ ]] && echo "yes" || echo "no" 查看左侧变量字符中是否包含右侧字符,包含则输出yes,不包含则输出no 2、* 0到多个 grep 'ro*' /etc/passwd 匹配0到多个r和o连在一起的字符,如ro、rro、oor、rrr、ooo 3、\<词首定位符 \>词尾定位符 grep -E "\<r" /etc/passwd 过滤出来R或r开头的单词,一个标点符号代表一个间断 grep -E "t\>" /etc/passwd 过滤出来以t结尾的单词,一个标点符号代表一个间断 4、^ 以什么开头 grep -E "^root" /etc/passwd 以root开头的语句 5、$ 以什么结尾 grep -E "bash$" /etc/passwd 以bash结尾的语句 6、. 匹配单个字符 grep "r..t" /etc/passwd 匹配r开头,中间是任意两字符,t结尾的单词 7、.* 任意多个字符 grep "r.*t" /etc/passwd 过滤出来以r开头,t结尾,中间是任意字符,任意个数的单词 8、[] 匹配方括号中任意一个字符 grep -E "[Rr]oot" /etc/passwd 过滤出包含root和Root的单词 9、[] 匹配指定范围的一个字符 grep [a-z]oot /etc/passwd 过滤出a-z中任何一个后缀是oot的单词 10、[^] 取反,匹配不在括号中的所有字符 11、\(\) 匹配后的标签 :%s/^\(.*\)\(:x:.*:\)\(.*\)/\3\2\1/g 使passwd中的文件,第一个:前的数据;第一个:后,最后一个:前的数据;最后一个:后的数据。调整一下位置,第一个整体代表数字1,第二个整体代表数字2,第三个整体代表数字3。使最后一段的位置放到第一个,第一段的位置放到最后一段,效果呈现 例子: grep -E "\<^[Rr].*[Nn]$\>" /etc/passwd 过滤出来以Rr开头,中间是任何字符,Nn结尾的语句 grep -E或egrep 1、+ 匹配一个或多个前导字符 egrep 'ro+t' /etc/passwd 匹配r开头,o是1到多次,t结尾的单词,r可以是无数次,o也可以是无数次,但是顺序不能变 2、? 匹配零个或一个前导字符 egrep 'ro?t' /etc/passwd 匹配r开头,o是零到一次,t结尾的单词,r和o都只能是一次的单词 3、a|b 匹配a或b egrep "root|alice" /etc/passwd 匹配passwd中包含root或alice的单词 4、x{m} 字符x重复m次 创建文件 =========== love love. loove looooove =========== 输入:egrep 'o{2}' a.txt 输出重复2的倍数o的单词,2、4、6、8个的二月孙 输出:loove 输入:egrep 'o{2,}' a.txt 输出o重复出现两次以上的单词 输出:loove 输出:looooove 输入:egrep 'o{6,7}' a.txt 输出o出现大于6次,小于7次的单词 输出: 没有包含的语句,则什么都不输出 例子: ps aux | grep nginx | grep -v grep 判断nginx是否启动,如果能过滤出来,返回值为0,代表启动。 如果过滤不出来,返回值步为0,代表nginx未启动. echo $? -v 反向过滤,如果没有-v nginx,会将grep nginx过滤nginx的进程过滤出来,返回值还是0,将grep过滤掉,只过滤nginx
12、shell编程-sed
非交互编辑器-sed
sed "参数" '模式' 参数 -f 指定一个规则文件 -n 阻止输入行输出 -r 扩展正则 -i 确认修改 模式 s 替换 g 整行 d 删除 p 打印 a 追加 i插入 参数: -f 编辑一个sed的规则文件,一行代表一个规则 vim a =========== s/d/b/g s/q/p/g s/w/m/g =========== sed -f a 需要修改的文件名 -n 阻止行输入输出 sed -n 's/要替换什么/替换成什么/g' 文件名 -n基本搭配模式中的p执行,如果没有-n,则会将文件中所有的内容全部输出一遍,在输出一遍筛选的内容,-n则是将全部输出的内容删除 -r 扩展正则 sed -r 's/[0-9]+/@/g' 文件名 +号是正则表达式,使中间没有其他字符的数字成为一个整体,将数字替换成@ -i 确认修改 sed -i.txt 's/a/b/g' 文件名 文件中所有的a替换成b并保存,再创一个新文件,保存原来的文件,并以.txt结尾 模式: s 替换 g 整行 sed 's/a/b/' 文件名 将文件中的第一个a替换成b sed 's/a/b/g' 文件名 将文件中所有的a替换成b(全局替换) d 删除 sed '1d' 文件名 删除文件的第一行 sed '1,2d' 文件名 删除文件从第一到第二行 sed '2,$d' 文件名 删除文件从第二行到最后一行 sed '/a/d' 文件名 删除文件中带有a的行 sed '/a/,2d' 文件名 删除文件中第一个a到第二个a的行数 sed '1~2d' 文件名 删除文件中,从第一行开始,每隔两行删除一行(删除奇数行) sed '0~2d' 文件名 删除文件中,从第零行开始,每隔两行删除一行(删除偶数行) sed '/a/,/b/d' 文件名 删除文件从带有a的行数到b的行数 sed '^#.*/d;/^$/d' 文件名 删除文件中#开头的,和空行(^$代表空行) a 追加 i 插入 sed '/在哪一行下方插入/a\需要插入的内容/' 文件名 匹配到的下方插入 sed '/在哪一行上方插入/i\需要插入的内容/' 文件名 匹配到的上方插入
作业题
非交互式编辑器,一次处理一行内容 测试文件 cat >> a.txt << EOF 0001 李梅 女 23 销售部 2040.00 0002 陈霞 女 20 销售部 1879.70 0003 程亮 男 26 销售部 2045.30 0004 刘辉 男 24 销售部 1915.00 0005 周波 男 21 销售部 1820.00 0006 苏健 女 20 销售部 1725.00 0007 苏康 男 26 销售部 2210.00 0008 王红 女 23 销售部 2043.00 0009 张三 男 28 销售部 2111.00 0010 李小 男 26 销售部 1850.00 0011 汤宏 男 29 销售部 1964.30 0012 田歌 女 24 销售部 1927.00 0013 李乐 男 26 销售部 2020.10 0014 丁丁 女 23 销售部 2120.00 0015 郑艳 女 22 销售部 1985.00 0016 许丽 女 25 销售部 1887.80 0017 崔霞 女 26 销售部 1973.30 0018 白亮 男 24 销售部 2564.60 >EOF sed 's/销售部/技术部/' a.txt 将所有的销售部字段,全部修改成技术部 sed 's/销售部/技术部/;s/男/女/' a.txt 以;隔开,一次修改多个字段 sed -i.bak 's/销售部/技术部/' a.txt -i确认修改,.bak添加备份文件 sed '/刘辉/d' a.txt 删除字段 sed '/刘辉/,$d' a.txt 匹配到第一个刘辉删除到最后 sed '1,/刘辉/d' a.txt 从第一行删除到匹配到的第一个刘辉 sed '/刘辉/,/张三/d' a.txt 从匹配到第一个刘辉删除到匹配到第一个张三中间全部删除 sed -r 's/[0-9]+/aaa/g' -r支持正则,[0-9]+匹配多个数字,g全局,将所有数字替换为aaa vim b.sed ----------- s/张/王/g s/女/男/g ----------- sed -f b.sed a.txt 将规则写进文件里执行,-f使用文件执行命令 sed -n '/男/p' a.txt -n阻止输入行输出,p打印选中的字段,如果没有-n,会将文本先全部打印一遍,再打印一遍选择的字段 sed '/丁丁/a\佳明' a.txt 在a.txt文件中的丁丁下面添加佳明 sed '/丁丁/i\佳明' a.txt 在a.txt文件中的丁丁上面添加佳明 sed '1~2d' a.txt 只显示奇数行 sed '0~2d' a.txt 只显示偶数行 sed '/#.*/d' a.txt 删除#后面的内容,基本都是删除配置文件中注释的 sed '/^$/d' a.txt 删除所有的空行 测试题: -------------------------- 静夜思 #提示 【作者】思乡 床前明月光。疑似地上霜, 举头望明月。低头思故乡, -------------------------- ==作业= 请将分割线上面的内容复制到文本中 1、将作者名字替换为李白 2、在作者行后面追加一列,唐朝 3、将,和。更换位置 4、将所有的,更换为, 5、将#开头的内容删除 6、将空行删除 7、将【】替换为[] -------------------------- 第一题:sed -i 's/作者/李白/' b.txt 第二题:sed -i '/【李白/a\唐朝' b.txt 第三题:sed -r -i 's/(.*)(。)(.*)(,)/\1\4\3\2/g' b.txt 第四题:sed -i 's/,/,/g' b.txt 第五题:sed -i '/^#.*/d' b.txt 第六题:sed -i '/^$/d' b.txt 第七题:sed -r -i 's/(【)(.*)(】)/\[\2\]/g' b.txt 修改ssh服务配置文件 拒绝root用户远程登录 sed -i 's/#PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config 修改端口22为2222 sed -i 's/#Port 22/Port 2222/' /etc/ssh/sshd_config 修改dns反解析为no sed -i 's/#UseDNS yes/UseDNS no/' /etc/ssh/sshd_config 修改nginx的配置文件 修改端口为80 sed -i 's/listen 80/listen 88;/' /etc/nginx/nginx.conf sed -i -r 's/listen\s+[0-9]{2}\;/Listen 88/gi' /etc/nginx/nginx.conf \s+ 多个空格,[0-9]{2} 0-9的数字出现2次,g 全局修改,i 不区分大小写 修改网站根目录为/opt/web sed -i -r 's/usr\/share\/nginx\/html/opt\/web/' /etc/nginx/nginx.conf 修改selinux的配置 将selinux设置为永久关闭 sed -i 's/SELINUX=.*/SELINUX=disabled/' /etc/selinux/config
13、shell编程-AWK
awk介绍
awk是行处理器:处理庞大文件时不会出现内存溢出或霍曼的问题 awk处理过程:依次对每一行进行处理,然后输出。默认分隔符是空格或tab键 awk的使用方法: awk 参数 '{处理内容}' awk 参数 'BEGIN{处理前要做的}{处理内容}END{处理后的内容}' 举例: 1、 输入:awk -F":" '{print $1,$3}' /etc/passwd 输出:root 0 bin 1 等等………… -F,用什么符号作为分隔符,print $1,$3打印分开段落的第一列内容和第三列内容,逗号默认是以空格隔开 2、 输入:awk 'BEGIN{print 1/2} {print "ok"} END{print "----"}' /etc/hosts 输出:0.5 ok ok ----
awk使用理解
NR:表示记录编号,当awk将行为记录时,该变量相当于当前行号 FNR:按不同的文件分开 NF:表示字段数量,当awk将行为记录时,该变量相当于当前列号 FS:输入字段分隔符,以什么符号区分割 OFS:输出字段分隔符,以什么分隔符显示 RS:输入记录分隔符 ORS:输出记录分隔符 FS(输入字段分隔符) awk 'BRGIN{FS=":"}{print $1}' /etc/passwd root bin …………等 OFS(输出字段分隔符) awk 'BEGIN{FS=":";OFS="%%"}{print $1,$2}' /etc/passwd root%%x bin%%x …………等 NR(添加行号,可以两个不同的文件将合并显示行号) awk -F: '{print NF,$0}' /etc/passwd /passwd 1 root:x:0:0:root:/root:/bin/bash 2 bin:x:1:1:bin:/bin:/sbin/nologin ………… 10 root:x:0:0:root:/root:/bin/bash 11 bin:x:1:1:bin:/bin:/sbin/nologin …………等 FNR(添加行号,两个不同的文件将分开行号显示) awk -F: '{print FNF,$0}' /etc/passwd /passwd 1 root:x:0:0:root:/root:/bin/bash 2 bin:x:1:1:bin:/bin:/sbin/nologin ………… 1 root:x:0:0:root:/root:/bin/bash 2 bin:x:1:1:bin:/bin:/sbin/nologin …………等 RS(输入记录分隔符) awk -F: 'BEGIN{RS="bin"}{print $0}' /etc/passwd root:x:0:0:root:/root:/ /bash :x:1:1: :/ :/s …………等 遇见bin就分隔 ORS(输出记录分隔符) awk -F: 'BEGIN{ORS="#"} {print $1,$2,$3}' /etc/passwd root x 0#bin x 1#daemon x 2#adm x 3#lp x 4#sync x 5#shutdown x 6#halt x 7#mail x 8#operator x 11#games x 12#ftp x 14#nobody x 99#systemd-network x 192#dbus x 81#polkitd x 999#sshd x 74#postfix x 89#chrony x 998#ntp x 38#nginx x 997#mysql x 27# 1、打印一个文件中的第二列和第三列 awk '{ print $2, $3}' 文件名 2、打印指定行指定列的某个字符 awk -F":" 'NR==3{ print $7 }' /etc/passwd 查看第三行。以:分隔的第七列 3、统计一个文件的行数 awk '{ print NR }' /etc/passwd 4、在脚本中传递变量到awk中 a=1000 echo | awk -v b=$a '{print b}' -v可以在awk中设置变量 5、指定字段分隔符-F或BEGIN{FS=":"} awk -F: '{ print $2,$3 }' /etc/passwd awk 'BRGIN{ FS=":"}{print $2,$3}' /etc/passwd 6、在awk中使用for循环 awk -F: '{for(i=1;i<=2;i++) {print $0}' /etc/passwd root:x:0:0:root:/root:/bin/bash root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin 分别打印每行每列 awk -F: '{ for(i=1;i<=NF;i++) {print $i}}' /etc/passwd root x 0 0 root /root /bin/bash bin x 1 1 bin /bin /sbin/nologin daemon x 2 2 daemon /sbin /sbin/nologin 7、在awk中使用if条件判断 显示管理员用户名 awk -F: '{if($3==0) {print $1 " is administrator."}}' /etc/passwd 以:分隔,第一列的第三个参数等于0,则打印 awk -F":" 'NR==3{if($3==2){print "对的"}}' /etc/passwd 以:分隔,第三列的第三个参数等于2,则打印 统计系统用户 awk -F":" '{if($3>0 && $3<1000){i++}} END{print i}' /etc/passwd 以:分隔,如果第三个参数大于0和小于100,则一直增加到1000以内,最后打印出i的值
14、shell编程expect
expect安装、语法
yum -y install expect 安装expect expect是一个免费的编程工具,用来实现自动交互任务,无需人为干预,用来实现自动交互功能的软件 用法: 1、定义脚本执行的shell #!/usr/bin/expect 类似于#!/bin/bash 2、set timeout 30 设置超时时间30s 3、spawn spawn是进入expect环境后才能执行的内部命令,不能直接在默认的shell环境中执行 功能:传递交互命令 4、expect 这里的expect同样是expect命令 功能:判断输出结果是否包含某项字符串,没有则立即返回,否则等待一段时间后返回,等待通过 timeout设置 5、send 执行交互动作,将交互要执行的动作进行输入给交互指令 命令字符串结尾要加上“r”,如果出现异常等待状态可以进行核查 6、interact 执行完后保持交互状态,把控制权交给控制台 如果不加这一项,交互完成会自动退出 7、exp_continue 继续执行接下来的操作 expect环境中设置变量用set,识别不了bash方式定义的变量 ================== #!/usr/bin/expect set a 1 spwan echo $a ================== chmod增加权限,./执行 输出;spawn echo 1 spawn执行shell环境的命令 免交互连接别人 1、 =============================================== #!/usr/bin/expect spawn ssh root@需要连接的ip expect { "yes/no" { send "yes\r";exp_continue } "password:" { send "连接ip的密码\r" } } interact 执行完后保持交互状态,把控制权交给控制台,不加这个交互完成后会自动退出 =============================================== 2、连接 yum -y install sshpass sshpass -p '1' ssh -o StricHostKeyCheking=no root@密码 拷贝别人的目录 sshpass -p '1' scp -o StricHostKeyCheking=no root@密码
不需要完全掌握,大部分免交互基本都有参数解决,实在解决不了照着下面的修改一下
expect环境中设置变量用set,识别不了bash方式定义的变量 错误方式: [root@newrain expect]# cat expect02.sh #!/usr/bin/expect user=22 spawn echo $user [root@newrain expect]# ./expect02.sh invalid command name "user=22" while executing "user=22" (file "./expect02.sh" line 3) 正确方式: [root@newrain expect]# cat ./expect02.sh #!/usr/bin/expect set user 22 spawn echo $user [root@newrain expect]# ./expect02.sh spawn echo 22 ============================================================================ [root@newrain expect]# cat expect01.sh #!/usr/bin/expect spawn ssh root@192.168.62.146 expect { "yes/no" { send "yes\r";exp_continue } "password:" { send "123\r" } } interact interact的作用测试 执行测试是否免交互: 要是用/usr/bin/expect的shell执行 [root@newrain expect]#/usr/bin/expect expect01.sh 成功 擦除最后一行interact进行测试 [root@newrain expect]#/usr/bin/expect expect01.sh 进入之后立即退出 ============================================================================ \r的作用测试 [root@newrain expect]# cat expect01.sh #!/usr/bin/expect set user root set pass 123 set ip 192.168.62.146 spawn ssh $user@$ip expect { "yes/no" { send "yes";exp_continue } "password:" { send "$pass" } } interact [root@newrain expect]# ./expect01.sh spawn ssh root@192.168.62.146 root@192.168.62.146's password: 加上\r之后 [root@newrain expect]# ./expect01.sh spawn ssh root@192.168.62.146 root@192.168.62.146's password: Permission denied, please try again. root@192.168.62.146's password: ============================================================================ 设置变量的方式 [root@newrain expect]# cat expect01.sh #!/usr/bin/expect set user root set pass 123 set ip 192.168.62.146 spawn ssh $user@$ip expect { "yes/no" { send "yes\r";exp_continue } "password:" { send "$pass\r" } } interact ============================================================================ 设置位置参数的方式(拓展) [root@newrain expect]# cat expect01.sh #!/usr/bin/expect set timeout 30 set user [ lindex $argv 0 ] set pass [ lindex $argv 1 ] set ip [ lindex $argv 2 ]S spawn ssh $user@$ip expect { "yes/no" { send "yes";exp_continue } "password:" { send "$pass\r" } } interact 运行结果为: [root@newrain expect]# ./expect01.sh root 123 192.168.62.146 spawn ssh root@192.168.62.146 root@192.168.62.146's password: Last login: Thu Aug 15 23:26:59 2019 from 192.168.62.136
#!/usr/bin/expect set username hello # 定义变量 set passwd 1 spawn ssh $username@172.17.138.107 # 执行交互式命令 expect { # 捕捉系统返回,与系统进行交互 "yes/no" { send "yes\r";exp_continue } "password:" {send "${passwd}\r"} } expect "*]$" # 捕捉系统返回,与系统进行交互 send "touch /tmp/abcaaa\r" send "ls /\r" expect eof interact
脚本例子
sh a.sh 和bash一样,不会影响终端的内容 bash a.sh 和sh一样,不会影响终端的内容 . a.sh 和source一样,会影响终端的内容 source a.sh 和.一样,会影响终端的内容 chmod a+x 授权 ./执行需要授权,这个方式什么脚本都可以执行 ./a.sh 不会影响终端的内容,但是可以执行所有脚本 举例: vim /root/a.sh ---------- cd /opt ls ---------- 环境均在/root下执行 sh a.sh 执行完终端的环境还是在当前目录root下 bash a.sh 同上 . a.sh 执行完终端的环境在opt下,脚本会影响终端的环境 source a.sh 同上 ./a.sh 执行完终端的环境还是在当前目录root下,可以执行所有脚本,但是需要授权 --------------------------------------------------------------------------------- 预定义变量: $$的脚本例子,当前进程的进程号(pid) ----------------- echo "此程序将会运行半个小时,如果提前终止,可以执行括号内的指令(kill -9 $$)" sleep 1800 ----------------- $!的例子,后台运行的最后一个进程的进程号(pid) sleep 100 & echo $! kill -9 $! ====================== 环境变量,所有地方都可以使用,脚本,其他bash中,都可以使用该变量 export a=b vim /etc/profile中添加变量,也可以是环境变量 source /etc/profile执行该文件,使环境变量生效 ====================== 自定义变量,就是普通变量 a=b ====================== 位置变量,要给传值 vim s.sh --------------- echo "我叫$1,我今年$2岁" --------------- sh s.sh 佳明 18 执行结果:我叫佳明,我今年18岁 --------------- ping -c1 -w1 $1 &>/dev/null && \ echo "到达 $1 ok" || \ echo "到达 $1 error" --------------- sh s.sh baidu.com 执行结果:到达 $1 ok 为baidu.com可以ping通 执行结果:到达 $1 error 则代表baidu.com不可以ping通 ===================== 脚本例子,脚本文件名为a.sh ------------------------------ time=`date +%y年%m月%d日-%H:%M` echo $time ------------------------------ sh s.sh 直接运行即可,会显示当前时间 ------------------------------ ip=`ifconfig ens33 | grep "inet" | awk 'NR==1{print $2}'` echo "主机的ip是:" $ip ------------------------------ sh s.sh 直接运行会显示当前ip ------------------------------ nowtime=$(uptime | awk '{print $1}') echo 当前时间为:$nowtime ------------------------------ sh s.sh 直接运行会显示当前时间,uptime,显示系统已经运行了多久、当前有多少用户登录以及系统负载的平均值。 ------------------------------ username=$(w -h | awk '{print $1}' | sort | uniq -c | awk '{print $2}') echo 当前登陆的用户是:$username ------------------------------ sh s.sh 直接运行会显示当前登录的用户,w命令用于显示已经登陆系统的用户列表,并显示用户正在执行的指令,详细解释在下面,-h只包含用户登录的相关信息,sort将重复的字符串排序在一起,uniq -c去重,将重复的记在一起,前面标上次数, ------------------------------ num=$RANDOM read -p "请输入一个数字:" num2 [ $num -lt $num2 ] && echo 你的数字大于$num || echo "你的数字小于$num" ------------------------------ sh s.sh 1000 $RANDOM是随机产生一个数字,范围在0-32767,-lt小于 要求该脚本必须在/opt下执行 ---------------------- pwd=$(cd $(dirname $0) ; pwd ) 得到脚本的确切位置 [ "$pwd" = /opt ] && echo "在/opt下" || echo "不在/opt下" ---------------------- ---------------------- read -p "请输入一个数字:" n if [ $n -gt 100 ];then echo "你输入的数字大于100" else echo "你输入的数字小于100" fi ---------------------- -gt 大于 ------------------------------ read -p "请输入你的年龄:" age if [ -z "$age" ];then echo "你输入的内容为空" exit 1 fi if [[ ! "$age" =~ ^[0-9]+$ ]];then 判断如果输入的值不是数字,则执行,=~匹配,^[0-9]代表匹配一个或多个数字,$代表结束 echo "你输入的不是数字" exit 2 fi if [ $age -le 18 ];then echo "你是一个未成年人" elif [ $age -le 40 ];then echo "你是一个青年" elif [ $age -le 60 ];then echo "你是一个中年人" elif [ $age -gt 60 ];then echo "你是一个老年人" else echo "外星人" fi ------------------------------ sh s.sh 执行完后输入数字进行匹配 ------------------------------ if [[ -d /var/lib/mysql || -n `ls /var/lib/mysql` ]];then echo "mysql已安装,且未进行初始化操作" else echo "mysql未准备就绪" fi ------------------------------ 如果/var/lib/mysql有这个,且是目录和/var/lib/mysql下内容不为空,则执行mysql已安装,否则执行mysql未准备就绪 -d 存在且是目录,-n 输入的值不会空则返回值为0 ------------------------------ #!/usr/bin/bash net="192.168.253." for i in {2..254} do { ip=${net}${i} ping -w1 -c1 $ip &>/dev/null if [ $? -eq 0 ];then echo "$ip up" else echo "$ip down" fi }& done wait 当有需要放到后台执行的任务时,脚本运行结束,自动退出。不加wait需要自己按回车结束 ------------------------------ 批量ping主机ip ------------------------------ read -p "请输入用户名:" user id $user if [ $? -eq 0 ];then echo "用户$user 存在" else echo "用户$user 不存在" fi ------------------------------ 一个一个判断用户是否存在 ------------------------------ if [ $# -lt 1 ];then echo "使用脚本方法:sh $0 需要检查的用户,使用空格隔开" exit else for i in $@ do echo "正在查询$i 中" sleep 1 #这两段可以不加,纯粹显得高级 id $i &>/dev/null if [ $? -eq 0 ];then echo $i 已存在 else echo $i 不存在 fi done fi ------------------------------ sh s.sh root jiaming 批量判断,空格隔开 ------------------------------ neihe=$(uname -a | awk '{print $3}' | awk -F"." '{print $1,$2}' | awk '{print $1}') banben=$(uname -a | awk '{print $3}' | awk -F"." '{print $1,$2}' | awk '{print $2}') if [[ $neihe -eq 3 && $banben -ge 9 ]];then echo "内核等于3,版本大于9" else echo "有误" fi ------------------------------ 判断和 ------------------------------ read -p "请输入用户前缀:" name read -p "请输入是删除还是创建:" modify read -p "请输入用户的个数:" num if [[ ! -n "$name" && ! -n "$modify" && ! -n "$num" ]];then echo "输入内容有误,程序结束" exit fi if [[ $modify = "删除" ]];then cmd="/usr/sbin/userdel -r" else cmd="/usr/sbin/useradd" fi for i in $(seq 1 $num) do $cmd $name$i &>/dev/null && \ echo $name$i 已${modify}成功 || \ echo $name$i ${modify}失败 done ------------------------------ ! -n如果输入的内容为空,返回值为0,该地方的作用是,如果输入的内容为空,则执行。没有用到返回值,! -n和-z是一个意思。&&为成功则执行,||为失败则执行
脚本安装mysql
#!/usr/bin/bash password="Qianfeng@123" cat << EOF 功能: 1、yum安装mysql5.7、8.0版本 2、初始化功能 3、清理安装环境 EOF rpm -qa | egrep "mysql|mariadb" &>/dev/null if [ $? -eq 0 ];then read -p "当前环境已安装mysql,是否清理[Y|N]" result if [ "$result" = Y ] || [ "$result" = "y" ];then echo "开始清理mysql环境" systemctl stop mysqld mariadb &>/dev/null yum remove -y `rpm -qa | egrep "mysql|mariadb"` &>/dev/null rm -rvf /var/log/mysqld.log /etc/my.cnf /var/lib/mysql-files /var/lib/mysql/ else echo "无法继续执行安装操作,程序终止" exit fi fi yum clean all && yum makecache if [ $? -eq 0 ];then echo "开始安装mysql" yum install -y https://dev.mysql.com/get/mysql80-community-release-el7-11.noarch.rpm &>/dev/null && \ read -p "请选择安装版本的版本号[80|57](default 57):" level level=${level:=57} if [ "$level" -eq "57" ];then yum install -y mysql-community-server --nogpgcheck --enablerepo mysql57-community --disablerepo mysql80-community elif [ "$level" -eq "80" ];then yum install -y mysql-community-server --nogpgcheck fi else echo "yum源有问题,请检查" fi systemctl enable mysqld --now if [ -f /var/log/mysqld.log ];then passwd=$(grep password /var/log/mysqld.log | awk 'END {print $NF}') mysqladmin -uroot -p"$passwd" password "$password" cat <<EOF mysql $level 安装完成 数据目录位置:/var/lib/mysql 命令位置:/usr/bin 配置文件位置:/etc/my.cnf 日志文件目录:/var/log/mysqld.log 历史数据位置:/tmp/mysql_data 初始密码:Qianfeng@123 EOF else echo "日志文件未找到,修改密码失败" fi
脚本高级编译安装nginx
mkdir nginx cd nginx vim color.sh -------------------------------- #!/usr/bin/bash function error(){ echo -e "\e[31m[`date '+%H:%M:%S'`]$@\e[0m" } sleep 0.5 function warning(){ echo -e "\e[33m[`date '+%H:%M:%S'`]$@\e[0m" } sleep 0.5 function success(){ echo -e "\e[32m[`date '+%H:%M:%S'`]$@\e[0m" } sleep 0.5 -------------------------------- vim lib.sh -------------------------------- #!/usr/bin/bash function networkCheck(){ local host host=$1 host=${host:=www.baidu.com} ping -w1 -c1 $host &>/dev/null if [ $? -eq 0 ];then success "网络检查正常" else error "网络检查异常" fi } function yumCheck(){ yum clean all &>/dev/null yum makecache &>/dev/null if [ $? -eq 0 ];then success "yum 检查正常" else error "yum 检查异常" fi } function checkUser(){ local user user=$1 user=${user:=root} if [ "$USER" = "$user" ];then success "当前执行用户为$user" else error "用户不匹配,当前用户为$user" fi } function firewalld(){ systemctl disable firewalld --now getenforce &>/dev/null if [ $? -eq 0 ];then success "防火墙,selinux已关闭" else setenforce 0 sed -i 's/SELINUX=.*/SELINUX-disabled/g' /etc/selinux/config success "未关闭,已设置关闭防火墙,selinux" fi } -------------------------------- vim nginx_install.sh -------------------------------- #!/usr/bin/bash source ./color.sh source ./lib.sh nginx_level=1.16.1 nginx_install_dir=/usr/local/nginx warning "此脚本编译安装nginx,会清理yum安装程序" function check(){ networkCheck yumCheck checkUser } function install(){ yum -y install gcc make zlib-devel pcre pcre-devel openssl-devel wget && \ wget -c http://nginx.org/download/nginx-$1.tar.gz && \ tar xzf nginx-$1.tar.gz && cd nginx-$1 && \ ./configure --user=nginx --group=nginx --prefix=$2 && make && make install && \ success "编译安装完成" || error "编译安装失败" } function init(){ useradd nginx -s /sbin/nologin echo "export PATH=$PATH:$1/bin" > /etc/profile.d/nginx.sh $1/sbin/nginx && success "nginx 启动成功" || error "nginx 启动失败" firewalld } check install $nginx_level $nginx_install_dir init $nginx_install_dir -------------------------------- sh nginx_insta
邮件脚本
vim mail.sh -------------------------------- #!/bin/bash filelist="/etc/group /etc/sudoers /etc/passwd /etc/sysctl.conf /etc/hosts /etc/resolv.conf" hashfile_path=/tmp/.hashcheck hashstatus='init' status=$hashstatus function init(){ md5sum $filelist > $hashfile_path } function check(){ result=$(md5sum -c $hashfile_path 2>/dev/null | grep FAILED) if [ -z "$result" ];then hashstatus="ok" echo "[$(date)] 重要文件防篡改程序:检查正常" >> /var/log/filecheck-`date "+%F"`.log else hashstatus="error" echo "[`date`] 重要文件防篡改程序:检查异常!!!" >> /var/log/filecheck-`date "+%F"`.log fi if [ $status != $hashstatus ];then echo $result | mail -s "[$(hostname)] 文件防篡改程序状态 $hashstatus" 18392971625@163.com status=$hashstatus fi } if [ "$1" = "init" ];then init else if [ ! -f $hashfile_path ];then echo "请先执行初始化操作: sh $0 init" else while : do check sleep 5 done fi fi -------------------------------- sh mail.sh init 初始化,将文件的哈希值输入到文件中 sh mail.sh 运行脚本,每五秒循环一次 tailf /var/log/filecheck-`date "+%F"`.log 运行脚本的时候,查看日志 需要开启三个终端,一个看日志,一个运行脚本,一个负责修改文件,当第一次修改或md5sum哈希值发生修改的时候
脚本安装php
VERSION_ID=$(cat /etc/redhat-release | sed -r 's/.* ([0-9]+)\..*/\1/') yum remove -y remi-release yum install -y https://mirrors.ustc.edu.cn/remi/enterprise/remi-release-${VERSION_ID}.rpm sed -e 's|^mirrorlist=|#mirrorlist=|g' \ -e 's|^#baseurl=http://rpms.remirepo.net|baseurl=https://mirrors.ustc.edu.cn/remi|g' \ -i /etc/yum.repos.d/remi*.repo yum clean all yum makecache id nginx &>/dev/null && { phpUser='nginx' } || { id www 2>/dev/null || useradd -s /sbin/nologin www phpUser='www' } # ----------------------------------------------- # version="56" version="74" # version="82" yum install -y php${version} php${version}-php-fpm php${version}-php-mysqlnd php${version}-php-bcmath php${version}-php-pdo php${version}-php-openssl php${version}-php-gd php${version}-php-mbstring php${version}-php-xml php${version}-php-mcrypt php${version}-php-zip && { if [ ! -f "/etc/opt/remi/php${version}/php-fpm.d/www.conf.bak" ]; then \cp /etc/opt/remi/php${version}/php-fpm.d/www.conf /etc/opt/remi/php${version}/php-fpm.d/www.conf.bak fi echo "[www] user = ${phpUser} group = ${phpUser} listen = /dev/shm/php-fpm-${version}.sock listen.allowed_clients = 127.0.0.1 listen.owner = ${phpUser} listen.group = ${phpUser} listen.mode = 0666 pm = dynamic pm.max_children = 50 pm.start_servers = 20 pm.min_spare_servers = 20 pm.max_spare_servers = 50 slowlog = /var/opt/remi/php${version}/log/php-fpm/www-slow.log php_admin_value[error_log] = /var/opt/remi/php${version}/log/php-fpm/www-error.log php_admin_flag[log_errors] = on php_value[session.save_handler] = files php_value[session.save_path] = /var/opt/remi/php${version}/lib/php/session php_value[soap.wsdl_cache_dir] = /var/opt/remi/php${version}/lib/php/wsdlcache" >/etc/opt/remi/php${version}/php-fpm.d/www.conf systemctl restart php${version}-php-fpm systemctl enable --now php${version}-php-fpm chown -R ${phpUser}:${phpUser} /var/opt/remi/php${version}/lib/php/session chown -R ${phpUser}:${phpUser} /var/opt/remi/php${version}/lib/php/wsdlcache }
命令集合解释
w命令 用于显示已经登陆系统的用户列表,并显示用户正在执行的指令。执行这个命令可得知目前登入系统的用户有哪些人,以及他们正在执行的程序。单独执行w命令会显示所有的用户,也可指定用户名称,仅显示某位用户的相关信息。 USER 列显示了登录用户的用户名。 TTY 列显示了用户使用的终端类型,通常是物理终端(如tty1、tty2等)或伪终端(如pts/0、pts/1等,这些通常用于SSH或终端仿真器连接)。 FROM 列显示了远程主机的地址(如果适用),或者如果用户在本地登录则可能是空的。 LOGIN@ 列显示了用户登录到系统的时间。 IDLE 列显示了用户空闲的时间长度。 JCPU 列显示了与终端关联的进程所使用的CPU时间。 PCPU 列显示了当前进程所使用的CPU时间。 WHAT 列显示了用户当前正在执行的命令。 sort命令用于对文本文件的行进行排序,并将结果输出到标准输出(通常是终端)或重定向到文件中。sort命令可以对文本文件中的字符串或数字进行排序,支持多种排序选项和参数。 以下是一些常用的sort命令选项: -n:按照数字大小进行排序,而不是按照字符顺序。 -r:反向排序,即从大到小或从Z到A。 -t:指定字段分隔符,用于指定非默认的字段分隔符(默认为制表符)。 -k:按照指定的字段进行排序。例如,-k 2表示按照第二列进行排序。 -u:去除重复的行。 -o:将排序结果输出到指定的文件中,而不是标准输出。 uniq 是一个在 Unix 和 Unix-like 系统(如 Linux)中常用的命令行工具,用于从排序后的文本文件中删除重复的行。如果不先对输入进行排序,uniq 命令可能无法正确识别所有重复的行,因为它只比较相邻的行。 uniq [OPTION]... [INPUT [OUTPUT]] INPUT 是要处理的文件。如果省略,uniq 会从标准输入读取。 OUTPUT 是可选的,指定输出文件的名称。如果省略,uniq 会将结果发送到标准输出。 常用选项: -c 或 --count:在每行前加上该行在输入文件中出现的次数。 -d 或 --repeated:仅显示重复的行。 -D 或 --all-repeated:对于每组重复的行,显示每一行。 -f 或 --skip-fields=N:忽略每行的前 N 个字段。字段是由空格分隔的。 -i 或 --ignore-case:比较时不区分大小写。 -s 或 --skip-chars=N:忽略每行的前 N 个字符。 -u 或 --unique:仅显示唯一的行(即不重复的行)。 -z 或 --zero-terminated:以空字符(\0)而不是换行符来分隔行。这通常在处理以空字符结尾的文件名列表时很有用。 tr "tr" 通常在不同的上下文中有不同的含义,但最常见的是指 Unix/Linux 系统中的一个命令行工具,名为 tr(translate 或 delete characters)。这个工具用于对从标准输入读取的字符进行替换、删除或压缩。 以下是一些 tr 的基本用法示例: 替换字符: --------------------------------- echo "hello world" | tr 'o' 'O' # 输出: hEllO wOrld --------------------------------- 删除字符: --------------------------------- echo "hello world" | tr -d 'o' # 输出: hell wrld --------------------------------- 压缩字符: --------------------------------- echo "hello world" | tr -s ' ' # 输出: hello world --------------------------------- 转换大小写: --------------------------------- echo "HELLO WORLD" | tr '[:upper:]' '[:lower:]' # 输出: hello world ---------------------------------
其他提取数值
怎么取根分区磁盘使用率及剩余内存: df -h | grep root | awk '{print $(NF-1)}' | cut -d% -f1 返回结果:5 (不加最后一段cut,则会显示5%) free -m | awk '/^Mem/{print $4}' 返回结果:3608 ====================================== 怎么取ip地址: ip -f inet a show dev ens33 -f指要显示的网络地址族,inet指显示ipv4的地址族,show代表要显示的信息,dev要显示哪个网络接口信息,ens33显示ens33的网络结构信息 返回结果: 2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 inet 192.168.253.131/24 brd 192.168.253.255 scope global noprefixroute ens33 valid_lft forever preferred_lft forever 提取ip地址: ip -f inet a show dev ens33 | awk 'NR==2{print $2}' | cut -d/ -f1 返回结果:192.168.253.131 cut 从文本中截取指定列的命令 -d以什么字段分隔 -f要打印哪个字段 ====================================== 哪个用户登录过终端: w -h | awk '{print $1}' | sort | uniq -c | awk '{print $2}' 显示哪个用户登录过终端 sort 将出现的字符全部排序 uniq -c 将出现的字符统计次数,并且归结为一类