shell脚本编程 ①
目录
- 脚本注释规范
- 调试脚本
- 脚本变量
- set命令和脚本安全
- 格式化输出
- 算数运算
- 逻辑运算
- 条件测试命令
- 变量测试
- 数值测试
- 字符串测试
- 文件测试
- 关于 ( ) 和 { }
- read接收输入
脚本注释规范
#!/bin/bash # 脚本使用的shell #--------------------------- #filename: test.sh # 脚本名 #version: 1.1 # 脚本版本 #data: 2022-03-16 # 修改日期 #author: david # 作者信息 #desc: test bash # 脚本描述 #email: 123.qq.com # 联系方式等 #qq: 1234567 #----------------------------
调试脚本
写完脚本之后可以使用bash命令调试
bash -n test.sh # 只检测脚本中的语法错误,不真正执行脚本
bash -x test.sh # 调试并执行脚本
脚本变量
变量表示命名的内存空间,将数据放到内存中,通过变量名引用
变量分为内置变量和用户自定义变量
- 内置变量: 系统内存在的变量,如PS1,PATH,UID,HOSTNAME,PPID,$?等
- 用户自定义变量: 用户根据需要自己创建的变量
变量命名要求
- 区分大小写
- 不能用程序中的保留字或者内置变量
- 只能用数字、字母或下划线,且不能以数字开头
变量的定义和引用
定义变量:
name='liudehua' # 直接赋值给变量 name=$PATH # 引用另一个变量 name=`ls` # 命令引用
引用变量:
$name ${name} echo '$name' # 单引号,强引用,变量会保持原字符串 echo "$name" # 双引号,弱引用,会被正常引用
删除变量
unset name
环境变量
- 可以使子进程继承父进程的变量,但是无法让父进程使用子进程的变量
- 一旦子进程修改从父进程继承的变量,将新的值传递给孙子进程
- 环境变量一般只在系统配置文件中使用,在脚本中较少使用
在当前shell创建一个普通变量,再打开新的bash子进程,发现此时变量无效。要想使变量在子进程中生效,就需要创建环境变量
[root@blog ~]# name=test [root@blog ~]# bash # 打开子shell进程 [root@blog ~]# echo $name # 变量名无效 [root@blog ~]# exit [root@blog ~]# export name=test1 # 退回到父shell创建环境变量 [root@blog ~]# bash # 重新进入到子shell [root@blog ~]# echo $name # 再次查看 # echo $BASHPID 显示当前shellID # pstree -p 显示进程树
创建环境变量
export name=value
declare -x name=value
显示所有环境变量
env
printenv
export
declare -x
查看指定进程的环境变量
cat /proc/$PID/environ
删除变量
unset name
只读变量
只能声明定义,后续不能修改和删除的变量
声明只读变量
readonly name1=value declare -r name=value
查看只读变量
readonly [-p] declare -r
位置变量
在脚本代码中调用通过命令行传递给脚本的参数
$1, $2, ... 对应第1个、第2个等参数,shift [n]换位置 $0 命令本身,包括路径 $* 传递给脚本的所有参数,全部参数合为一个字符串 $@ 传递给脚本的所有参数,每个参数为独立字符串 $# 传递给脚本的参数的个数 注意:$@ $* 只在被双引号包起来的时候才会有差异
清空所有位置变量
set --
退出状态码变量
表示执行程序对应的相应状态 $?
$?的值为0 # 代表成功 $?的值是1到255 # 代表失败
exit [n] # 在脚本中通过使用exit自定义状态码
脚本安全和set
set命令,可以定制shell环境,用在脚本里,能实现一些安全机制
set + 选项 # 关闭对应功能 set - 选项 # 开启对应功能
set命令通过修改 $-变量 实现定制shell环境
h:hashall,打开选项后,Shell 会将命令所在的路径hash下来,避免每次都要查询。通过set +h将h选项关闭 i:interactive-comments,包含这个选项说明当前的 shell 是一个交互式的 shell。所谓的交互式shell,在脚本中,i选项是关闭的 m:monitor,打开监控模式,就可以通过Job control来控制进程的停止、继续,后台或者前台执行等 B:braceexpand,大括号扩展 H:history,H选项打开,可以展开历史列表中的命令,可以通过!感叹号来完成,例如“!!”返回上最近的一个历史命令,“!n”返回第 n 个历史命令
将set选项写在脚本开头位置,实现脚本安全
set -u # 在使用一个未定义的变量时,报错 set -e # 脚本中命令执行结果非0,退出脚本
格式化输出
在脚本里为了实现格式化输出,一般使用printf打印,使用格式如下:
printf "指定的格式" "文本1" "文本2" ...
常用格式替换符
%s # 字符串 %d,%i # 十进制整数 %f # 浮点数 %c # ASCII字符,对应参数的第一个字符 %b # 相对应的参数中包含转移字符时,可以用此替换符替换,对应的转义字符会被转义 %o # 八进制 %u # 不带正负号的十进制值 %x # 十六进制(a-f) %X # 十六进制(A-F) %% # 表示%本身
%#s 中的数字代表此替换符中的输出字符宽度,不足补空格,默认时右对齐,若左对齐用-表示,例如:%-10s
%03d 表示3位宽度,不足前边用0补全
常用转义字符
\a # 警告字符
\b # 后退
\f # 换页
\n # 换行
\r # 回车
\t # 水平制表符
\v # 垂直制表符
\ # 表示\本身
算数运算
bash里的算数运算只支持整数,不支持小数,算数运算不能直接使用,需要用到特殊表达方式。如下:
(1) let var=算术表达式 (2) ((var=算术表达式)) 和上面等价 (3) var=$[算术表达式] (4) var=$((算术表达式)) (5) var=$(expr arg1 arg2 arg3 ...) (6) declare -i var = 数值 (7) echo '算术表达式' | bc
内建的随机数生成器变量
echo $RANDOM # 取值范围:0-32767 echo $[RANDOM%50] # 获取0-49之间的随机数
逻辑运算
& 与运算:与0相与结果为0,和1相与结果保留原值,一假则假,全真才真
| 或运算: 和1相或结果为1,和0相或结果保留原值,一真则真,全假才假
!非运算: 取反
^ 异或: 异或的两个值,相同为假,不同为真。两个数字x,y异或得到z,z再与任意两者之一X异或。将得出另一个值y
利用异或实现变量互换
x=10;y=20;x=$[x^y];y=$[x^y];x=$[x^y]
短路运算
短路与 &&
CMD1 && CMD2 第一个CMD1结果为真(1), 第二个CMD必须参与运算 第一个CMD1结果为假(0), 总的结果必定为0,不需要执行CMD2
短路或 ||
CMD1 || CMD2 第一个CMD1结果为真(1),总的结果必定为1,不需要执行CMD2 第一个CMD1结果为假(0),第二个CMD2必须要参与运算,才能得到最终结果
条件测试命令
- 若条件测试为真,则状态码变量$? 返回0
- 若条件测试为假,则状态码变量$? 返回1
条件测试命令
- test 表达式
- [表达式] # 和test等价,建议使用[ ]
- [[表达式]] # 相当于[ ]的增强版,在支持[ ]的基础上,支持扩展正则表达式和通配符
变量测试
[ -v NAME ] # 判断NAME变量是否定义 [ -R NAME ] # 判断NAME变量是否定义并且是名称引用,bash 4.4 新特性 # 注意 [ ] 需要空格
数值测试
- -eq 是否等于
- -ne 是否不等于
- -gt 是否大于
- -ge 是否大于等于
- -lt 是否小于
- -le 是否小于等于
字符串测试
字符串测试主要使用 test 和 [ ]
test -z $str # 判断字符串是否为空,没定义或空为真 [ -z "$str" ] test -n $str # 判断字符串是否不空,不空为真 [ -n "$str" ] test $str # 判断字符串是否不空,不空为真 [ "$str" ] test $str1 = $str2 # 判断两个字符串是否相同 [ $str1 = $str2 ] test $str1 != $str2 # 判断两个字符串是否不同 [ $str1 != $str2 ] test $str1 > $str2 # 根据两个字符串的ascii比较大小
[[ ]]
[[ expression ]] 用法 == 左侧字符串是否和右侧的PATTERN相同 注意:此表达式用于[[ ]]中,PATTERN为通配符 =~ 左侧字符串是否能够被右侧的正则表达式的PATTERN所匹配 注意: 此表达式用于[[ ]]中;扩展的正则表达式
文件测试
文件存在性测试
-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
关于 ( ) 和 { }
(CMD1;CMD2;...)和 { CMD1;CMD2;...; } 都可以将多个命令组合在一起,批量执行
- (list) 会开启子shell,并且list中变量赋值及内部命令执行后,将不影响后续的环境
- {list} 不会开启子shell,在当前shell中运行,会影响当前shell
read 接受输入
read用来把输入值分配给一个或多个shell变量,read从标准输入中读取值,每个单词分配个一个变量。所有剩余单词分配个最后一个变量,如果变量名没有指定。则赋值给系统内置变量REPLY
常用参数
-p 指定要显示的提示 -s 静默输入,一般用于密码 -n N 指定输入的字符长度N -d '字符' 输入结束符 -t N TIMEOUT为N秒