目录
1 shell脚本的基本用法
1.1 shell脚本的用途
- 自动化常用命令
- 执行系统管理和故障排除
- 创建简单的应用程序
- 处理文本或文件
1.2 shell脚本的注释规范
- 第一行一般为调用使用的语言
- 程序名,避免更改文件名为无法找到正确的文件
- 版本号
- 更改后的时间
- 作者相关信息
- 该程序的作用,及注意事项
- 最后是各版本的更新简要说明
1.3 第一个shell脚本
范例:第一个shell脚本hello word
脚本执行方法:
执行远程主机脚本:
curl -s http://10.0.0.100/hello.sh|bash
wget -qO - http://10.0.0.100/testdir/hello.sh |bash
备份脚本:
1.4 shell脚本调试
只检测脚本中的语法错误,但无法检查出命令错误,但不真正执行脚本
bash -n script.sh
调试并执行,常用于检查脚本中的命令错误
bash -x
脚本中常见的三种错误:
- 语法错误,会导致后续的命令不继续执行,可以用bash -n 检查错误,提示的出错行数不一定是准确的。
- 命令错误,默认后续的命令还会继续执行,用bash -n 无法检查出来 ,可以使用 bash -x 进行观察。
- 逻辑错误:只能使用 bash -x 进行观察。
1.5 变量
变量表示命名的内存空间,将数据放在内存空间中,通过变量名引用,获取数据
1.5.1 变量的类型
变量有两种类型:
- 内置变量:PS1,PATH,UID,HOSTNAME等
- 用户自定义变量
变量的数据类型:
- 字符
- 数值:整型、浮点型,bash 不支持浮点数
1.5.2 变量的命令规则
- 不能使程序中的保留字:如:if, for
- 只能使用数字、字母及下划线,且不能以数字开头,注意:不支持短横线 “ - ”,和主机名相反
- 见名知义,用英文单词命名,并体现出实际作用,不要用简写,如:ATM
- 统一命名规则:驼峰命名法, studentname,大驼峰StudentName 小驼峰studentName
- 变量名大写:STUDENT_NAME
- 局部变量小写
- 函数名小写
1.5.3 变量的定义与使用
变量的生效范围等标准划分变量类型:
- 普通变量:生效范围为当前shell进程;对当前shell之外的其它shell进程,包括当前shell的子shell进程均无效
- 环境变量:生效范围为当前shell进程及其子进程
- 本地变量:生效范围为当前shell进程中某代码片断,通常指函数
变量的赋值:
name='value'
value 可以是以下多种形式:
- 直接字串:name='root'
- 变量引用:name="$USER"
- 命令引用:name=`COMMAND` 或者 name=$(COMMAND)
注意:变量赋值是临时生效,当退出终端后,变量会自动删除,无法持久保存,脚本中的变量会随着脚本结束,也会自动删除。
变量的引用:
$name
${name}
弱引用和强引用:
- "$name " 弱引用,其中的变量引用会被替换为变量值
- '$name ' 强引用,其中的变量引用不会被替换为变量值,而保持原字符串
范例:变量追加值
范例:利用变量实现动态命令
显示已定义的所有变量
set
删除变量
unset <name>
范例:显示系统信息的脚本
#!/bin/bash
# ------------------------------------------
# Filename: hello.sh
# Version: 1.0
# Date: 202024/06/23
# Author: yang
# Email: 123456@qq.com
# Website: www.baidu.com
# Description: This is the first script
# Copyright: 2024 yang
# License: GPL
# ------------------------------------------
RED="\E[1;31m"
GREEN="\E[1;32m"
END="\E[0m"
echo -e "$GREEN----------------------Host systeminfo--------------------$END"
echo -e "HOSTNAME: $RED`hostname`$END"
echo -e "IPADDR: $RED` ifconfig ens33|grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' |head -n1`$END"
echo -e "OSVERSION: $RED`cat /etc/redhat-release`$END"
echo -e "KERNEL: $RED`uname -r`$END"
echo -e "CPU: $RED`lscpu|grep 'Model name'|tr -s ' '|cut -d : -f2`$END"
echo -e "MEMORY: $RED`free -h|grep Mem|tr -s ' ' : |cut -d : -f2`$END"
echo -e "DISK: $RED`lsblk |grep '^sd' |tr -s ' ' |cut -d " " -f4`$END"
echo -e "$GREEN---------------------------------------------------------$END"
1.5.4 环境变量
环境变量说明:
- 可以使子进程继承父进程的变量,但是无法让父进程使用子进程的变量
- 一般只在系统配置文件中使用,在脚本中较少使用
变量声明和赋值:
#声明并赋值
export name=VALUE
declare -x name=VALUE
#或者分两步实现
name=VALUE
export name
显示所有的环境变量:
env
printenv
export
declare -x
常见的bash内置环境变量:
PATH
SHELL
USER
UID
HOME
PWD
SHLVL #shell的嵌套层数,即深度
LANG
MAIL
HOSTNAME
HISTSIZE
_ #下划线 表示前一命令的最后一个参数
1.5.5 只读变量
只读变量:只能声明定义,但后续不能修改和删除,即常量
声明只读变量:
readonly name
declare -r name
查看只读变量:
readonly [-p]
declare -r
1.5.6 位置变量
位置变量:在bash shell中内置的变量, 在脚本代码中调用通过命令行传递给脚本的参数
$1, $2, ... 对应第1个、第2个等参数,shift [n]换位置
$0 命令本身,包括路径
$* 传递给脚本的所有参数,全部参数合为一个字符串
$@ 传递给脚本的所有参数,每个参数为独立字符串
$# 传递给脚本的参数的个数
注意:$@ $* 只在被双引号包起来的时候才会有差异
清空所有位置变量:
set --
范例:rm命令的安全实现
#!/bin/bash
# ------------------------------------------
# Filename: hello.sh
# Version: 1.0
# Date: 202024/06/23
# Author: yang
# Email: 123456@qq.com
# Website: www.baidu.com
# Description: This is the first script
# Copyright: 2024 wang
# License: GPL
# ------------------------------------------
WARNING_COLOR="echo -e \E[1;31m"
END="\E[0m"
DIR=/tmp/`date +%F_%H-%M-%S`
mkdir $DIR
mv $* $DIR
${WARNING_COLOR}Move $* to $DIR $END
1.5.7 退出状态码变量
进程执行后,将使用变量 $? 保存状态码的相关数字,不同的值反应成功或失败,$?取值范例 0-255。
$?的值为0 #代表成功
$?的值是1到255 #代表失败
范例:
用户可以在脚本中使用以下命令自定义退出状态码:
exit [n]
注意:
- 脚本中一旦遇到exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字。
- 如果未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码。
1.5.8 脚本安全和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 命令实现脚本安全
- -u 在扩展一个没有设置的变量时,显示错误信息, 等同set -o nounset
- -e 如果一个命令返回一个非0退出状态值(失败)就退出, 等同set -o errexit
- -o option 显示,打开或者关闭选项
- 显示选项:set -o
- 打开选项:set -o 选项
- 关闭选项:set +o 选项
- -x 当执行命令时,打印命令及其参数,类似 bash -x
1.6 格式化输出print
格式:
printf "指定的格式" "文本1" ”文本2“……
常用格式替换符:
替换符 | 功能 |
%s | 字符串 |
%f | 浮点格式 |
%b | 相对应的参数中包含转义字符时,可以使用此替换符进行替换,对应的转义字符会被转 义 |
%c | ASCII字符,即显示对应参数的第一个字符 |
%d,%i | 十进制整数 |
%o | 八进制值 |
%u | 不带正负号的十进制整数 |
%x | 十六进制值(a-f) |
%X | 十六进制值(A-F) |
%% | 表示%本身 |
常用转义字符:
1.7 算数运算
shell 支持算术运算,但只支持整数,不支持小数
bash中的算术运算:
- +
- -
- *
- /
- % 取模,即取余数,示例:9%4=1,5%3=2
- ** 乘方
实现算术运算:
(1) let var=算术表达式
(2) ((var=算术表达式)) 和上面等价
(3) var=$[算术表达式]
(4) var=$((算术表达式))
(5) var=$(expr arg1 arg2 arg3 ...)
(6) declare –i var = 数值
(7) echo '算术表达式' | bc
内建的随机数生成器变量:
- $RANDOM 取值范围:0-32767
范例:取0-49的随机数和生成随机颜色的字符串
增强型赋值:
+= i+=10 相当于 i=i+10-= i-=j
相当于 i=i-j
*=
/=
%=
++ i++,++i -- i--,--i
相当于 i=i+1
相当于 i=i-1
1.8 条件测试命令
条件测试:判断某需求是否满足,需要由测试机制来实现,专用的测试表达式需要由测试命令辅助完成测试过程,若条件真,则状态码变量 $? 返回0 若假,则状态码变量 $? 返回。
条件测试命令
- test EXPRESSION
- [ EXPRESSION ] #和test 等价,建议使用 [ ]
- [[ EXPRESSION ]]
- 注意:EXPRESSION前后必须有空白字符
1.8.1 变量测试
#判断 NAME 变量是否定义
[ -v NAME ]
#判断 NAME 变量是否定义并且是名称引用,bash 4.4新特性
[ -R NAME ]
1.8.2 数值测试
-eq 是否等于
-ne 是否不等于
-gt 是否大于
-ge 是否大于等于
-lt 是否小于
-le 是否小
1.8.3 字符串测试
test 和 [ ] 用法
-z 字符串是否为空,没定义或空为真,不空为假
-n 字符串是否不空,不空为真,空为假
STRING1 = STRING2 是否等于,注意 = 前后有空格
STRING1 != STRING2 是否不等于
> ascii码是否大于ascii码
< 是否小于
[[]] 用法,建议,当使用正则表达式或通配符使用,一般情况使用 [ ]
== 左侧字符串是否和右侧的PATTERN相同
注意:此表达式用于[[ ]]中,PATTERN为通配符
=~ 左侧字符串是否能够被右侧的正则表达式的PATTERN所匹配
注意: 此表达式用于[[ ]]中;扩展的正则表达式
1.8.4 文件测试
文件存在性测试:
-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
1.8.5 关于()和{}
(CMD1;CMD2;...)和 { CMD1;CMD2;...; } 都可以将多个命令组合在一起,批量执行
- ( list ) 会开启子shell,并且list中变量赋值及内部命令执行后,将不再影响后续的环境, 帮助参看:man bash 搜索(list)
- { list; } 不会启子shell, 在当前shell中运行,会影响当前shell环境, 帮助参看:man bash 搜索{ list;
1.8.6 短路与或非
COMMAND1 && COMMAND2 #并且,短路与,代表条件性的AND THEN
如果COMMAND1 成功,将执行COMMAND2,否则,将不执行COMMAND2
COMMAND1 || COMMAND2 #或者,短路或,代表条件性的OR ELSE
如果COMMAND1 成功,将不执行COMMAND2,否则,将执行COMMAND2
! COMMAND #非,取反
COMMAND1 && COMMAND2 || COMMAND3
如果 COMMAND1 成功 执行 COMMAND2 反之执行 COMMAND3
1.9 使用read命令来接受输入
使用read来把输入值分配给一个或多个shell变量,read从标准输入中读取值,给每个单词分配一个变 量,所有剩余单词都被分配给最后一个变量,如果变量名没有指定,默认标准输入的值赋值给系统内置 变量REPLY。
常见选项:
- -p 指定要显示的提示
- -s 静默输入,一般用于密码
- -n N 指定输入的字符长度N
- -d '字符' 输入结束符
- -t N TIMEOUT为N秒
范例: read和输入重定向
2 bash配置文件
2.1 按生效范围划分两类
全局配置:
/etc/profile
/etc/profile.d/*.sh
/etc/bashrc
个人配置:
~/.bash_profile
~/.bashrc
2.2 按功能划分
profile类为交互式登录的shell提供配置
全局:/etc/profile, /etc/profile.d/*.sh
个人:~/.bash_profile
功能:
- 用于定义环境变量
- 运行命令或脚本
bashrc类:为非交互式和交互式登录的shell提供配置
全局:/etc/bashrc
个人:~/.bashrc
功能:
- 定义命令别名和函数
- 定义本地变量
2.3 配置文件生效
修改profile和bashrc文件后需生效两种方法:
- 重新启动shell进程
- source | . 配置文件
2.4 Bash 退出任务
保存在~/.bash_logout文件中(用户),在退出登录shell时运行
功能:
- 创建自动备份
- 清除临时文件
3 流程控制之条件选择
3.1 选择执行 if 语句
格式:
if 判断条件1; then
条件1为真的分支代码
elif 判断条件2; then
条件2为真的分支代码
elif 判断条件3; then
条件3为真的分支代码
...
else
以上条件都为假的分支代码
fi
说明:
- 多个条件时,逐个条件进行判断,第一次遇为“真”条件时,执行其分支,而后结束整个if语句 if 语句可嵌套。
范例:身体质量指数 (BMI)
#!/bin/bash
# ------------------------------------------
# Filename: hello.sh
# Version: 1.0
# Date: 202024/06/23
# Author: yang
# Email: 123456@qq.com
# Website: www.baidu.com
# Description: This is the first script
# Copyright: 2024 wang
# License: GPL
# ------------------------------------------
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
3.2 条件判断case语句
格式:
case 变量引用 in
PAT1)
分支1
;;
PAT2)
分支2
;;
...
*)
默认分支
;;
esac
case支持glob风格的通配符:
*: 任意长度任意字符
?: 任意单个字符
[]:指定范围内的任意单个字符
|: 或,如 a或b
范例:运维菜单实现
#!/bin/bash
# ------------------------------------------
# Filename: hello.sh
# Version: 1.0
# Date: 202024/06/23
# Author: yang
# Email: 123456@qq.com
# Website: www.baidu.com
# Description: This is the first script
# Copyright: 2024 wang
# License: GPL
# ------------------------------------------
echo -en "\E[$[RANDOM%7+31];1m"
cat <<EOF
请选择:
1)备份数据库
2)清理日志
3)软件升级
4)软件回滚
5)删库跑路
EOF
echo -en '\E[0m'
read -p "请输入上面数字1-5: " MENU
case $MENU in
1)
echo "备份数据库"
;;
2)
echo "清理日志"
;;
3)
echo "软件升级"
;;
4)
echo "软件回滚"
;;
5)
echo "删库跑路"