bash脚本编程:
脚本程序:解释器解释执行
shell: shell是交互式接口;为用户提供了编程环境;能够提供一些内部命令,并且能通过PATH环境变量找到外部命令;把命令提交给内核启动为进程。
编程环境:
流程控制语句:顺序执行;循环执行;选择执行;
条件测试:真、假
使用命令: echo $?
命令的状态结果:0: 真 ;1-255: 假
过程式的编程语言的元素:变量、流程、函数、数组
变量:局部变量、本地变量、环境变量、位置参数变量、特殊变量
变量:a)数值型:整型、浮点型、布尔型
b)字符型:字符串
注:bash变量是弱类型;默认字符型;
变量引用:${VAR_NAME}
引号:
弱引用:""
强引用:''
命令引用:``
声明某变量为整型变量:
let VAR_NAME=VALUE
declare -i VAR_NAME=VALUE
声明某变量为环境变量:
export VAR_NAME=VALUE
declare -x VAR_NAME=VALUE
脚本的编写格式:
第一行:写明解释器; #!/bin/bash
注释行:所有以#开头的行均为注释行;会被解释器忽略;
执行脚本:
1)赋予执行权限;指明路径执行;
2)直接传递脚本给bash解释器
bash的选项:
-n: 测试脚本中是否有语法 错误;
-x: 调试执行;
算术运算四种方式:
1)$[EXPRESSION]
2)let VAR_NAME=EXPRESSION
3)$((EXPRESSION))
4)$(expr argu1 argu2 argu3)
流程控制之一:for循环
for VAR in LIST; do
STATEMENT1
...
done
循环次数:为列表中的元素的个数;
LIST:列表,包含至少一个元素的字符串集合;
(1) 直接给出;
(2) 数值列表:a){start..end},例如:{1..10}
b) seq [start [step]] end
(3) 返回列表的命令;
(4) globbing;
(5) 变量引用:$*, $@
例如:添加9个用户,user1,...user9,密码同用户名。
for的几种特殊情况:
(1) for省略,会自动获取脚本参数列表;
(2) C编程风格:
for ((变量赋值;循环条件;修正表达式)); do
CMD1
CMD2
done
(3) 循环嵌套:
for i in [LIST1]; do
CMD1
for j in [LIST2]; do
CMD2
...
done
done
例如:写一个脚本: ping 172.16.X.Y内的所有主机。(循环嵌套)
bash命令退出和退出状态码:
命令在bash中执行结束退出时,其执行成功与否可通过退出状态码来记录;脚本的退出状态码取决于执行的最后一条命令;
如何自定义退出状态码?
使用命令:exit # 成功为0;失败则为:1-255
注意:提前退出脚本,也可使用exit命令实现;
bash之条件测试:if/then结构
条件测试:
test EXPRESSION
[ EXPRESSION ]
[[ EXPRESSION ]]
COMMAND
测试表达式:
(1) 整数测试;
(2) 字符串测试;
(3) 文件测试
整数测试:A, B
A -gt B: 大于
A -ge B: 大于等于
A -eq B: 等于
A -lt B: 小于
A -le B: 小于等于
A -ne B: 不等于
字符串测试:A, B
A > B
A < B
A >= B
A <= B
A == B或A = B:等值比较
A != B: 不等于
-z A: 判断A是否为空;空则为真,不空则假;
-n A:判断A是否不空;不空则为值,空则为假;
条件取反:
! CONDITION
条件测试语法:
单分支的if语句:
if CONDITION; then
CMD1
CMD2
...
fi
例如:传递一个参数给脚本,而后以此参数为用户名,添加此用户;
if语句嵌套:
if CONDITION1; then
if CONDITION2; then
CMD
fi
fi
例如:传递一个参数给脚本,而后以此参数为用户名,不存在时,则添加此用户;
双分支的if语句:
if CONDITION-TRUE; then
分支1
else
分支2
fi
例如:写一个脚本:(1) 传递两个整数参数给脚本;(2) 返回其较大一个;
多分支的if语句:
if CONDITION1-TRUE; then
分支1
elif CONDITION2-TRUE; then
分支2
elif CONDITION3-TRUE; then
分支3
...
else
分支n
fi
例如:传递一个用户名参数给脚本:
(1) 如果用户的id号为0,则显示为管理员;
(2) 如果用户的id号大于6000,则显示为guest;
(3) 如果用户的id号大于500,则显示为普通用户;
(4) 如果用户的id号大于0, 则显示为系统用户;
(5) 否则,无法识别;
COMMAND用作条件:
(1) 使用命令执行结果;
(a) 使用命令引用
(b) 使用比较操作符
例如:用户UID小于500
方式1、[ `id -u $username` -lt 500 ]
方式2、 userid=`id -u $username`
[ $userid -lt 500 ]
(2) 使用命令的退出状态码
(a) 先运行命令;
(b) 退出状态码
两种引用方式:
(a) if COMMAND; then
注意:COMMAND不能被命令引用;COMMAND的执行结果通常没有意义,所以其结果通常(&>)被定向至/dev/null
(b) 先执行命令,后判断退出状态码是否为0
COMMAND
if [ $? -eq 0 ]
条件测试:
字符串测试:=~
"$A" =~ PATTERN
注:如果变量A中保存的字符串能被PATTERN所匹配;即为真;否则为假;
文件测试:$file
-e $file: 是否存在;存在则为真;
-a $file: 同上;弃用;
-f $file: 文件是否存在,且为普通文件;
-d $file: 是否存在且为目录;
-h $file: 是否存在且为符号链接文件;
-L $file:同上
-b $file: 是否存在且为块设备文件;
-c $file: 是否存在且为字符设备文件;
-S $file: 是否存在且为套接字文件:
-p $file: 是否存在且为管道文件;
-r $file: 当前用户对此文件是否拥有读权限;
-w $file: 当前用户对此文件是否拥有写权限;
-x $file: 当前用户对此文件是否拥有执行权限;
-u $file: 文件是否拥有suid权限;
-g $file:文件是否拥有sgid权限;
-k $file: 文件是否拥有sticky权限;
-O $file: 当前用户是否为文件的属主;
-G $file: 当前用户是否属于文件的属组;
-N $file: 文件自从上一次被读取之后,是否被修改过;
$f1 -nt $f2: 文件f1是否比文件f2新;
$f1 -ot $f2: 文件f1是否比文件f2旧;
$f1 -ef $f2: f1和f2是否为同一个文件的硬链接;
例如:写一个脚本,传递一个文件路径参数给脚本;
(1) 如果脚本无参数,则显示必须给至少一个参数,退出脚本;退出码5;
(2) 路径指向的文件如果不存在,则直接退出脚本;退出码为6;
(3) 判断文件类型:
(a) 如果是普通文件,则显示为"common file";
(b) 如果是目录,则显示为"directory";
(c) 如果是符号链接,则显示为"symbolic link file";
(d) 如果是块设备,则显示为“block device file";
(e) 如果是字符设备,则显示为"character device file";
(f) 否则,则显示为“unkown”;
组合测试条件:给条件添加逻辑操作符;
或, -o: [ -z "$hostname" -o "$hostname" == 'localhost' ]
与, -a: [ $uid -gt 0 -a $uid -lt 500 ]
非:[ ! EXPRESSION ]
例如:传递一个用户名参数给脚本:
(1) 如果用户的id号为0,则显示为管理员;
(2) 如果用户的id号大于6000,则显示为guest;
(3) 如果用户的id号大于500,则显示为普通用户;
(4) 如果用户的id号大于0, 则显示为系统用户;
(5) 否则,无法识别;
组合测试条件:短路操作符
与:COMMAND1 && COMMAND2
COMMAND1的退出状态如果为假,则COMMAND2不用运行,即可有最终结果;
或:COMMAND1 || COMMAND2
COMMAND1的退出状态如果为真,则COMMAND2不用运行,即可有最终结果;
非:! COMMAND
交互式脚本:
read [OPTIONS] [name ...]
-p "PROMPT"
-t #: 超时时长
给变量以默认值:[ -z "$VAR" ] && VAR=VALUE
case语句:简洁版的多分支if语句;
语法格式:
case 变量引用 in
PATTERN1)
分支1
;;
PATTERN2)
分支2
;;
...
*)
分支n
;;
esac
注:PATTERN可使用通配符:*:任意长度的任意字符
?: 任意单个字符
[]:指定范围内的任意单个字符
a|b: a或者b
例如:显示如下菜单给用户
cpu) show cpu infomation;
mem) show memory infomation;
disk) show disk infomation;
*) quit
提示用户键入选项:
(1) cpu: 显示CPU相关的信息
(2) mem: 显示内存相关的信息
(3) disk: 列出磁盘设备
(4) 其它任何信息,即为退出脚本
case语句)
while、until循环
while循环:
while CONDITION; do
循环体
循环控制变量的修正表达式
done
例如:计算100以内所有偶数之和;
注:当CONDITION为“真”进入循环,直到“假”退出循环;
until循环:
until CONDITION; do
循环体
循环控制变量修正表达式
done
例如:计算100以内所有偶数之和;
注:当CONDITION为“假”时进入循环;为“真”退出循环;
循环控制命令:
break:提前退出循环;
break [N]: 退出N层循环;N省略时表示退出break语句所在的循环;
continue: 提前结束本轮循环,而直接进入下轮循环;
continue [N]:提前第N层的循环的本轮循环,而直接进入下轮循环;
语法格式:
while CONDITION; do
CMD1
if CONDITION2; then
break [N]
fi
CMD2
...
done
死循环:while)
while true; do
循环体
done
死循环:until)
until false; do
循环体
done
例如:写一个脚本,判断centos登录了当前系统;
(1) 如果登录了,则脚本终止;
(2) 每5秒种,查看一次用户是否登录;
while)
until)
while的特殊用法:遍历指定文件的每一行
while read line; do
循环体
done < /path/from/somefile
例如:统计/etc/passwd文件行数;