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秒