目录
初识shell
shell 定义
shell的分类和切换
使用场景
bash 初始化
bash shell 特性
shell 脚本
shell 脚本规范
变量的类型
脚本运行
变量置换
命令替换
变量替换
变量替换-匹配截取
索引及切片
basename & dirname
shell编程-流程控制
if判断与循环体
shell分支if语句
shell编程-循环结构
shell 循环控制shift、continue、break、exit
Shell 也是一种程序设计语言,它有变量,关键字,各种控制语句,有自己的语法结构,利用shell程序设计语 可以编写功能强、代码简短的程序 #! Shebang 定义解释器
# cat /etc/shells /bin/sh /bin/bash /sbin/nologin /usr/bin/sh /usr/bin/bash /usr/sbin/nologin 默认shell: bash shell centos中脚本使用的默认shell 为/usr/bin/sh 查看当前正在使用的shell #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脚本的源代码是直接可读,能被所有人看到的) 如果你需要有上面的任意一种应用,请考虑其他的更强大的脚本语言――Perl,Tcl,Python,Ruby,或者可能是其他更 高级的编译型语言,例如C,C++或者是Java
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可以做任何运维的事情(一切取决于业务需求)
用户登录时相关的bash配置文件 (登录脚本) 全局配置文件 /etc/profile /etc/profile.d/*.sh /etc/bashrc 个人配置文件 ~/.bash_profile ~/.bashrc profile类的文件: 设定环境变量 运行命令或脚本 bashrc类的文件: 定义命令别名 用户登录时加载bash配置文件的过程 登录式shell加载配置文件优先级显示 ~/.bash_profile --> ~/.bashrc --> /etc/bashrc --> /etc/profile --> /etc/profile.d/*.sh 非登录式shell加载配置文件优先级显示 ~/.bashrc --> /etc/bashrc --> /etc/profile.d/*.sh
图形模式登录时,顺序读取:/etc/profile 和 ~/.profile
图形模式登录后,打开终端时,顺序读取:/etc/bash.bashrc 和 ~/.bashrc
文本模式登录时,顺序读取:/etc/bash.bashrc,/etc/profile 和 ~/.bash_profile
从其它用户 su 到该用户,则分两种情况:
如果带 -l 参数(或-参数,--login 参数),如:su -l username,则 bash 是 login 的,它将顺序读取以下配置文件:/etc/bash.bashrc,/etc/profile 和~/.bash_profile。
如果没有带 -l 参数,则 bash 是 non-login 的,它将顺序读取:/etc/bash.bashrc 和 ~/.bashrc
注销时,或退出 su 登录的用户,如果是 longin 方式,那么 bash 会读取:~/.bash_logout
执行自定义的 Shell 文件时,若使用 bash -l a.sh 的方式,则 bash 会读取行:/etc/profile 和 ~/.bash_profile,若使用其它方式,如:bash a.sh,./a.sh,sh a.sh(这个不属于bash Shell),则不会读取上面的任何文件。
上面的例子凡是读取到 ~/.bash_profile 的,若该文件不存在,则读取 ~/.bash_login,若前两者不存在,读取 ~/.profile。
增加补全指令 #yum -y install bash-completion 查看系统环境变量 #env 修改历史命令记录数量,用于安全加固 #vim /etc/profile 下的historysize 可以修改 实用调用历史命令 上下健 esc . 上一条命令的最后一个参数 Ctrl+r 在历史命令中查找,输入关键字调出之前的命令 配置查看历史命令前缀带时间 vim /etc/profile export HISTTIMEFORMAT="[%Y.%m.%d %H:%M:%S]" source /etc/profile 命令前加空格不记录历史 vim /etc/profile export HISTCONTROL=ignorespace source /etc/profile 前后台作业 bg fg nohup忽略退出信号 管道 ls ./ | xargs -i cp {} /tmp 命令排序执行 ; 命令分割,在一行中执行多条语句 && 前成功后面再执行 || 前面不成功,后面再执行 通配符 *,?,[],{} * 匹配 0 或多个字符 ? 匹配任意一个字符 [list] 匹配 list 中的任意单一字符 [!list] 匹配 除list 中的任意单一字符 {} 集合查看 命令行常用快捷键 Ctrl+a 切换到命令行开始(跟home一样,但是home在某些unix环境下无法使用) Ctrl+e 切换到命令行末尾 Ctrl+r 在历史命令中查找,输入关键字调出之前的命令
shell脚本文件需要以.sh结尾 第一个原因,让别人认的这个是shell脚本,sh后缀编辑时有高亮显示。 拓展名后缀,如果省略.sh则不易判断该文件是否为shell脚本 #!/usr/bin/env bash ---shebang蛇棒, 解释器, # 执行脚本方式 1、 sh 脚本.sh 2、 bash 脚本.sh 3、 ./脚本.sh # 需要执行权限 4、 . 脚本.sh 5、 source 脚本.sh 子影响父 sh 和 source的区别: sh:当使用sh来执行脚本时,它会创建一个新的子进程来执行脚本,并且脚本中的变量和环境设置通常不会影响当前的shell环境 source:使用source或.来执行脚本文件时,脚本中的命令会在当前Shell环境中执行,并且脚本可以修改当前Shell的变量和环境设置。 例 编辑脚本 #cat a.sh #!/bin/bash pwd cd /opt pwd 更换执行脚本方式执行显示如下
变量
bash作为程序设计语言和其它高级语言一样也提供使用和定义变量的功能
预定义变量、环境变量、自定义变量、位置变量
预定义变量 $$ 当前进程PID $? 命令执行后的返回状态.0 为执行正确,非 0 为执行错误 $# 位置参数的数量 $* 所有位置参数的内容 $@ 所有的参数 $! 上一个后台进程的PID 自定义变量 定义:变量名称=值 变量名称:只能由字母,数字,下划线组成,不能以数字开头; 注意:应该让变量名称有意义; = 赋值符号 前后不能有空格 ; 值: 所有的字符串和数字都可以; 引用变量: $变量名 或 ${变量名}。 环境变量 shell在开始执行时已经定义好的 env 查看所有环境变量 set 查看所有变量 环境变量拥有可继承性:export之后就拥有继承性 export 导出变量(作用范围) 永久生效 写到4个登陆脚本中 ~/.bashrc ~/profile 更好放在/etc/profile.d/* 下建立独立的环境变量配置文件 常用环境变量:USER UID HOME HOSTNAME PWD PS1 PATH PATH:存储所有命令所在的路径 位置变量 $1 $2 $3 $... #/test.sh start #/test.sh 2 3 5 hello 变量运算 算式运算符: +、-、*、/、()、%取余(取模) 运算方式:$(()) $[] expr $(()) 例: # echo $(( 5+2-(3*2)/5 )) 6 $[] # echo $[ 5 + 2 - (3*2)/5 ] 6 expr # expr 5 + 3 #不能做浮点数运算,前后要加空格 8 乘法运算 expr 5 \* 8 40 或 expr 5 '*' 8 40 随机数 $RANDOM% 例 #!/bin/bash echo $(($RANDOM%50+1)) 这串代码实现了随机生成从1~50之间是数 为什么取余时需要+1是因为在取余时如果被 整除那么余数会是0,这样就不在限定范围内了 #浮点运算 bash本身不能做小数计算:需要bc命令转换 (yum安装) #echo "2*4" | bc #echo "2^4" | bc #echo "scale=2;6/4" | bc scale: 精度 圆周率精确到1000位 echo "scale=1000;4 * a(1)" | bc -l #注 -l定义使用的标准数学库 #变量引用 转义:\ 当一个字符被引用时,其特殊含义被禁止 把有意义的变的没意义,把没意义的变的有意义 \n换行 \t table 例 echo -e '5\n6\n7' 5 6 7 完全引用:'' //强引 硬引 部分引用:"" //弱引 软引 读取用户标准输入:read read:功能就是读取键盘输入的值,并赋给变量 参数注: -s '后面的内容是加密信息,不要输出' -t 超时时间 取消屏幕回显 #stty -echo #stty echo 变量嵌套(扩展) eval 执行字符串内的可执行命令 #name='kobe' #kobe=24 #eval echo '$'"${name}" 24 先获取name的值,通过再次构造echo命令,使用eval再一次执行语句,就达到我们的目的。
•sh –x script 这将执行该脚本并显示所有变量的值 •sh –n script 不执行脚本只是检查语法模式,将返回所有错误语法 •sh –v script 执行脚本前把脚本内容显示在屏幕上变量置换
a=`date +%m%d` a=$(date +%m%d) 反引号亦可用$() 代替
${parameter:-word} 若 parameter 为空或未设置,则用 word 代替 parameter 进行替换,parameter 的值不变 ${parameter:=word} 若 parameter 为空或未设置,则用 word 代替 parameter 进行替换,parameter 的值改变 ${parameter:+word} 若 parameter 设置了,则用 word 代替 parameter 进行替换,parameter 的值不变 ${parameter:?message} 若 parameter 为空或未设置,则 message 作为标准错误打印出来,这可用来检查变量是否正确设置
# 是去掉左边(在键盘上 # 在 $ 之左边) % 是去掉右边(在键盘上 % 在 $ 之右边) 单一符号是最小匹配;两个符号是最大匹配(贪婪匹配) 例: file=/dir1/dir2/dir3/my.file.txt ${file#*/}: 拿掉第一条 / 及其左边的字符串:dir1/dir2/dir3/my.file.txt ${file##*/}: 拿掉最后一条 / 及其左边的字符串:my.file.txt ${file#*.}: 拿掉第一个 . 及其左边的字符串:file.txt ${file##*.}: 拿掉最后一个 . 及其左边的字符串:txt ${file%/*}: 拿掉最后条 / 及其右边的字符串:/dir1/dir2/dir3 ${file%%/*}: 拿掉第一条 / 及其右边的字符串:(空值) ${file%.*}: 拿掉最后一个 . 及其右边的字符串:/dir1/dir2/dir3/my.file ${file%%.*}: 拿掉第一个 . 及其右边的字符串:/dir1/dir2/dir3/my
[root@newrain ~]# a=12345678 [root@newrain ~]# echo ${a:5} //从第5位开始截取 678 [root@newrain ~]# echo ${a:3:4} 4567 [root@newrain ~]# echo ${a:2:-1} 34567 [root@newrain ~]# echo ${a:2:-2} 3456
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 dirname 获取当前脚本的路径 $( cd $( dirname $0 ) ; pwd ) #判断脚本的位置 path=$( cd $( dirname $0 ) ; pwd ) [[ $path = '/root' ]] && echo "继续执行" || echo "请在/root目录下执行"
test 条件 条件为真返回 0,条件为假返回 1 [ 条件 ] test 能够理解3种类型的表达式 1.文件测试 2.字符串比较 3.数字比较 字符串 -n STRING # -n 字符串长度不为零 -z STRING # -z 字符串长度为0 STRING1 = STRING2 # = 判断两个字符串是否一样 STRING1 != STRING2 # != 判断两个字符串是否不一样 数字 eq 等于 ne 不等于 ge 大于等于 le 小于等于 gt 大于 lt 小于 文件 test -f 存在且是普通文件 # 重要 -d 存在且是目录 -h 存在且是符号链接 -b 块设备 -c 字符设备 -e 文件存在
流控制: •在一个shell脚本中的命令执行顺序称作脚本的流。大多数脚本会根据一个或多个条件来改变它们的流。 •流控制命令:能让脚本的流根据条件而改变的命令称为条件流控制命令 •exit语句:退出程序的执行,并返回一个返回码,返回码为0正常退出,非0为非正常退出,例如: •exit 0 If语句 if语句语法如下: if [ $1 -eq 1 ] then echo '等于1' elif [ $1 -eq 2 ] then echo '等于二' else echo '既不等一,也不等于二' fi 多个条件联合 逻辑与 if [ $condition1 ] && [ $condition2 ] if [ $condition -a $condition2 ] if [[ $condition1 && $condition2 ]] 逻辑或 if [ $condition1 ] || [ $condition2 ] if [ $condition -o $condition2 ] if [[ $condition1 || $condition2 ]] # test 和 [] 中 我们可以使用 [ $condition1 ] && [ $condition2 ] 或者 [ $condition -a $condition2 ] # 在 [[]] 这种情况,我们可以直接使用[[ $condition1 && $condition2 ]] 建议在if中直接使用[[]]这种方式,这种方式更加稳定。[[]] shell的一个命令。 -a && 逻辑与 and 两端的条件都可以成立 -o || 逻辑或 or 两端的条件有一段成立就行 case语句 case 语句是 shell 中流控制的第二种方式,语法如下: case $var in 模式1) 执行1 ;; 模式2) 执行2 ;; 模式3) 执行3 ;; *) 执行4 esac #命令;;表明流应该跳转到case语句的最后,类似C语言中的break指令。
shell循环-for语句 for i in {取值范围} # for 关键字 i 变量名 in 关键字 取值范围格式 空格 制表符 换行 do # do 循环体的开始 循环体 done # done 循环体的结束 shell循环while语句 while 条件 # while 关键字 条件 [ $1 -lt 10 ] ,while循环,条件为真的情况下,会循环 do 循环体 done 例 a=0 while [ $a -lt 100 ] do # a=$[ $a + 1 ] let a++ echo $a done shell循环until语句 until 条件 # 当后面的条件表达式,为假的时候进行循环,当他为真了就停止循环了。 do 循环体 done 例 a=0 until [ $a -gt 10 ] do let a++ echo $a done
shift命令 位置参数可以用shift命令左移。比如shift 3表示原来的$4现在变成$1,原来的$5现在变成$2等等,原来的$1、$2、$3丢 弃,$0不移动。不带参数的shift命令相当于shift 1。 对于位置变量或命令行参数,其个数必须是确定的,或者当 Shell 程序不知道其个数时,可以把所有参数一起赋值给变量 $*。 若用户要求 Shell 在不知道位置变量个数的情况下,还能逐个的把参数一一处理,也就是在 $1 后为 $2,在 $2 后面为 $3 等,则需要用shift把所有参数变成$1 break是立马跳出循环; 例 #!/bin/bash while : do echo -n "请输入1-5之间的数字 " read aNum case $aNum in 1|2|3|4|5) echo "您的数字是 $aNum!" ;; *) echo "您输入的数字不再1-5中间,游戏结束!" break ;; esac done continue它不会跳出所有循环,仅仅跳出当前循环; #!/bin/bash while : do echo -n "请输入1-5之间的数字 " read aNum case $aNum in 1|2|3|4|5) echo "您的数字是 $aNum!" ;; *) echo "您输入的数字不再1-5中间,游戏结束!";、 continue ;; esac done exit是直接退出整个脚本