Shell脚本编程
1.shell程序编程风格
面向过程语言
多一件事,排出个步骤,第一步干什么,第二步干什么,如果出现情况A,做什么处理,如果出现了情况B,做什么处理
问题规模小,可以步骤化,按部就班处理
以指令为中心,数据服务于指令
2.脚本创建过程
第一步:使用文本编辑器来创建文本文件
第一行必须包括shell声明序列: #!
#!/bin/bash
添加注释,注释以#开头
第二步:加执行权限
给予执行权限,在命令行上指定脚本的绝对或相对路径
第三步:运行脚本
直接运行解释器,将脚本作为解释器程序的参数运行
3.第一个脚本hello world
[21:25:28 root@aiyoubucuo ~]$cat /data/ybw.sh #!/bin/bash echo "hello world"
[21:28:30 root@aiyoubucuo ~]$. /data/ybw.sh
hello world
4.shell脚本调试
只检测脚本中语法错误,但无法检查出命令错误,但不真正执行脚本
bash -n /data/ybw.sh
调试并执行
bash -x /data/ybw.sh
5.Shell变量类型
内置变量,如:PS1,PATH,UID,HOSTNAME,BASHPID
用户自定义变量
不同的变量存放的数据不同,决定了以下
数据存储方式
参与的运算
表示的数据范围
6.变量的使用变量
使用一个定义过的变量,只要在变量名前面加美元符号即可
your="ybw"
echo $your
echo ${your_name}
#变量名外面的{花括号}是可选的,为了帮助解释器识别变量的边界
7.只读变量
使用 readonly 命令可以将变量定义为只读变量,只读变量的值不能被改变。
下面的例子尝试更改只读变量,结果报错:
#!/bin/bash
your="jommy"
readonly your
your="zxx"
运行脚本结果:
[root@localhost ~]# ./2.sh
./2.sh: line 4: your: readonly variable
8.删除变量
使用 unset 命令删除变量
unset jommy
变量被删除后不能再次使用。unset 命令不能删除只读变量。
以上运行没有任何输出结果
9.位置变量
位置变量:在bash shell中内置的变量,在脚本代码中调用通过命令行传递给脚本的参数
$1,$2 ... 对应第1个,第2个等参数,shift【n】换位置
$0 命令本身,包括路径
$* 传递给脚本的所有参数,全部参数整合为一个字符串
$@ 传递给脚本的所有参数,每个参数为独立字符串
$# 传递给脚本的参数的个数
echo "1st arg is $1" echo "2st arg is $2" echo "3st arg is $3" echo "10st arg is ${10}" echo "11st arg is ${11}" echo "the number of arg is $#" echo "all args are $*" echo "all args are $@" echo "the scriptime is `basename $0`" [13:33:22 root@aiyoubucuo ~]./arg.sh {a..z} 1st arg is a 2st arg is b 3st arg is c 10st arg is j 11st arg is k the number of arg is 26 all args are a b c d e f g h i j k l m n o p q r s t u v w x y z all args are a b c d e f g h i j k l m n o p q r s t u v w x y z the scriptime is arg.sh
10.退出状态码变量
$?的值为0 代表成功
$?的值是1-255 代表失败
[20:03:49 root@aiyoubucuo ~]curl http://www.baidu.com [20:04:02 root@aiyoubucuo ~]$? 0: command not found 访问百度结果是0 [20:04:11 root@aiyoubucuo ~]curl http://www.sdhfuiashdncieow.com curl: (6) Could not resolve host: www.sdhfuiashdncieow.com [20:04:43 root@aiyoubucuo ~]$? 6: command not found 瞎输入一个结果是6
用户可以在脚本中使用以下命令自定义退出状态码
exit [n]
脚本中一旦遇到exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字
如果为给脚本退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码
11.脚本安全和set
set命令可以来定制shell环境
-u 在扩展一个没有设置的变量时,显示错误信息
-e 如果一个命令返回一个非0退出状态值(失败)就退出
可以一起使用set -eu,但输出结果显示错误就退出,有时候不是那种真正的错误也会退出
12.格式化输出printf
printf “指定的格式” 文本1 文本2
%s:字符串
%f:浮点格式
常用转义字符
:换行
f:换页
:后退
[20:31:59 root@aiyoubucuo ~]printf "(%s) " 1 2 3 (1) (2) (3)
13.算数运算
bash只支持整数,不支持小数
var=$((算术表达式))
var=$[算数表达式]
$RANDOM 取值范围0-32767
14.逻辑运算
1,真
2,假
以上为二进制
[20:50:37 root@aiyoubucuo ~]true [21:06:40 root@aiyoubucuo ~]echo $? 0 [21:06:49 root@aiyoubucuo ~]false [21:06:56 root@aiyoubucuo ~]echo $? 1
短路与
CMD1 短路与 CMD2
第一个CMD1结果为真,第二个CMD2必须要参与运算,才能得到最终结果
第一个CMD1结果为假,总的结果必定为0,因此不需要执行CMD2
也可以用&&表示短路与
命令1成功执行命令2,否则不执行命令2
短路或
CMD1 短路或 CMD2
第一个CMD1结果为真,总的结果必定为0,因此不需要执行CMD2
第一个CMD1结果为假,第二个CMD2必须要参与运算,才能得到最终结果
也可以用||表示短路或
命令1成功将不执行命令2,否则将执行命令2
&&和||组合使用
如果有wang账户则执行echo命令,没有则执行创建用户命令
15.条件测试命令
若真,则状态码变量$?返回0
若假,则状态码变量$?返回1
test EXPRESSION
[EXPRESSION]和test等价
[[EXPRESSION]]用法
=~ 左侧字符串是否能够被右侧的征兆表达式匹配,支持正则表达式
[21:25:06 root@aiyoubucuo ~]#[[ $IP =~ ^(([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]
|25[0-5]).){3}([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$ ]]
[21:27:45 root@aiyoubucuo ~]#echo $?
1
判断ip是否合法
16.数值测试
-eq是否大于等于
-ne是否不等于
-gt是否大于
-ge是否大于等于
-lt是否小于
-le是否小于等于
[21:18:16 root@aiyoubucuo ~]i=10 [21:18:20 root@aiyoubucuo ~]j=8 [21:18:28 root@aiyoubucuo ~][ $i -gt $j ] [21:19:09 root@aiyoubucuo ~]echo $? 0
17.关于()和{}
两种符号都可以将多个命令组合在一起,批量执行
(list)会开启子shell,并且list中变量赋值及内部命令执行后,将不再影响后续的环境
{list} 不会开启子shell,在当前shell中运行,会影响当前shell环境
[21:36:58 root@aiyoubucuo ~]name=ybw;(echo $name;name=yang;echo $name);echo $name ybw yang ybw [21:37:06 root@aiyoubucuo ~]name=ybw;{ echo $name;name=yang;echo $name; } ;echo $name ybw yang yang
18.read
使用read命令来接受输入
使用read来把输入值分配给一个或多个shell变量,read从标准输入中读取值,给每一个单词分配一个变量,所有剩余单词被分配给最后一个变量,如果变量名没有指定,默认标准输入的值赋给系统内置变量reply
read 选项 【name...】
-p指定要显示的提示,常用
19.if
单分支
if 判断条件;then
条件为真的分支代码
fi
双分支
f 判断条件;then
条件为真的分支代码
else
条件为假的分支代码
fi
多分支
if 判断条件1;then
条件1为真的分支代码
elif 判断条件2;then
条件2为真的分支代码
elif 判断条件3;then
条件3为真的分支代码
...
else
以上条件都为假的分支代码
fi
例子
#!/bin/bash read -p "请输入身高(m为单位):" HIGH if [[ ! "$HIGH" =~ ^[0-2](.[0-9]{,2})?$ ]];then echo "输入身高错误" exit 1 fi read -p "请输入体重(kg为单位):" WEIGHT if [[ ! "$WEIGHT" =~ ^[0-9]{1,3}$ ]];then echo "输入体重错误" exit 1 fi BMI=`echo $WEIGHT/$HIGH^2|bc` if [ $BMI -le 18 ] ;then echo "太瘦了宝贝,多吃点" elif [ $BMI -lt 24 ] ;then echo "身材不错,想办法追我" else echo "你是猪吗宝贝" fi
19.条件判断case语句
case支持通配符
#!/bin/bash echo "1,备份数据库" echo "2.清理日志" echo "3.软件升级" echo "4.软件回滚"
read -p "请选择上面数字1-4:" MENU case $MENU in 1) echo "备份数据" ;; 2) echo "清理日志" ;; 3) echo "软件升级" ;; 4) echo "软件回滚" esac
20.for循环
依次将列表中的元素赋值给“变量名”,每次赋值后即执行一次循环体;知道列表中的元素耗尽,循环结束
如果省略【in words】,此时使用位置参量
#!/bin/bash sum=0 for i in {1..100} do let sum+=i done echo "sum=$sum"
for循环计算1..100
for ((控制变量初始化;条件判断表达式;控制变量的修正表达式))
do
循环体
#!/bin/bash for((sum=0,i=1;i<=100;sum+=i,i++)) do true done echo $sum
21.循环while
while commands; do commands; done
while condition; do
循环体
done
condition:循环控制条件;进入循环之前,先做一次判断;每一次循环之后会再次做判断;条件为真,则执行一次循环;直到条件测试状态假,则终止循环,因此:condtion一般应有循环控制变量;而此变量的值在循环体不断地被修正
进入条件:condition为true
退出条件:condition为假
无限循环
while true; do
循环体
done
while :; do
循环体
done
#!/bin/bash sum=0 i=1 while ((i<=100)) ;do let sum+=i;let i++ done echo $sum
while特殊用法while read
while read line;do
循环体
done < /path/from/somefile
说明:依次读取/path/from/somefile文件中的每一行,且将行赋值给变量line
[21:17:22 root@centos7 ~]$echo yang bo wen | while read x y z;do echo $x $y $z;done yang bo wen
22.循环控制语句continue
continue 【N】:提前结束第N层的本轮循环,而直接进入下一轮判断;最内层为第一层
for ((i=0;i<3;i++));do for ((j=0;j<4;j++));do [ $j -eq 2 ] && continue echo $j done echo ----------------------------- done 结束内圈循环
[23:43:06 root@centos7 ~]$bash coun.sh
0
1
3
-----------------------------
0
1
3
-----------------------------
0
1
3
-----------------------------
continue 2结束外圈循环
for ((i=0;i<3;i++));do for ((j=0;j<4;j++));do [ $j -eq 2 ] && continue 2 echo $j done echo ----------------------------- done 结束外圈循环 0 1 0 1 0 1
23.break
提前结束第N个循环
for ((i=0;i<3;i++));do for ((j=0;j<4;j++));do [ $j -eq 2 ] && break echo $j done echo ----------------------------- done 结束内圈循环 [23:51:35 root@centos7 ~]$bash coun.sh 0 1 ----------------------------- 0 1 ----------------------------- 0 1 -----------------------------
break 2
for ((i=0;i<3;i++));do for ((j=0;j<4;j++));do [ $j -eq 2 ] && break 2 echo $j done echo ----------------------------- done 直接结束外圈循环 0 1
24 循环与菜单select
select循环主要用于创建菜单,按数字顺序排列的菜单项显示在标准错误上,并显示PS3提示符等待用户输入
select是个无线循环,因此要用break命令退出循环,或用exit命令终止脚本。select经常和case联合使用。
[21:32:55 root@centos7 ~]$bash ybw.sh
1) 北京老鸭
2) 佛跳墙
3) 小龙虾
4) 羊蝎子
5) 火锅
6) 点才结束
请点菜(1-6):
#!/bin/bash
sum=0
PS3="请点菜(1-6):"
select MENU in 北京老鸭 佛跳墙 小龙虾 羊蝎子 火锅 点才结束;do
case $REPLY in
(1)
echo $MENU 价格是 100 let sum+=100 ;; (2) echo $MENU 价格是 88 let sum+=88 ;; (3) echo $MENU 价格是 66 let sum+=66 ;; (4) echo $MENU 价格是 166 let sum+=166 ;; (5) echo $MENU 价格是 200 let sum+=200 ;; (6) echo "点菜结束,退出" break ;; (*) echo "点菜错误,重新选择" ;; esac done echo "总价是:$sum"
25.函数function
函数function是由若干条shell命令组成的语句块,实现代码重用和模块化编程
它与shell程序形式上是相似的,不同的是他不是一个单独的进程,不能独立运行,而是shell程序的一部分
函数和shell程序区别
shell程序在子shell中运行
函数在当前shell中运行。因此在当前shell中,函数可对shell中变量进行修改
函数由两部分组成:函数名和函数体
declare -F 查看当前已定义的函数名 declare -f 查看当前已定义的函数定义 declare -f func_name 查看指定当前已定义的函数名 declare -F func_name 查看当前已定义的函数名定义
删除函数
unset func_name
交互式环境调用函数
[22:13:23 root@centos7 ~]$you() { > echo "i am Spider-Man" > } [22:17:14 root@centos7 ~]$you i am Spider-Man
实现判断Centos的主版本
[22:21:57 root@centos7 ~]$kan() { > sed -nr 's/.* ([0-9].[0-9]).*/1/p' /etc/redhat-release > } [22:37:02 root@centos7 ~]$kan 7.5
脚本中定义及使用函数
[22:42:11 root@centos7 ~]$bash li.sh now going to the function hello hello there today is 2021-01-03 back from the function #!/bin/bash hello() { echo "hello there today is `date +%F`" } echo "now going to the function hello" hello echo "back from the function"
函数在使用前必须定义,因此应将函数定义放在脚本开始部分,直至shell首次发现后才能使用,调用函数仅使用其函数名即可
source 接函数路径,输出函数名,即可调用函数