文章目录
- SHELL脚本
- 1.脚本调试
- 2.1变量赋值:
- 2.2显示已定义的所有变量:
- 2.3 删除变量
- 变量声明和赋值
- 显示所有环境变量:
- 只读变量
- 防止拓展
- 变量拓展
- 脚本安全和set
- set命令实现脚本安全(重点)
- 格式化输出printf
- 增强型赋值:
- 逻辑运算
- 穿插异或运算
- 文件权限测试
- ()和{}
- 组合测试条件
- 通配符eg:
- 正则表达式eg:
- 使用read命令来接受输入
- 1.分类
- 2.功能分类
- 3.Bash退出任务
- 格式2
- while循环
- until
- 循环控制语句continue
- 循环控制语句break
- 循环控制shift命令
- while特殊用法:while read
- 循环与菜单select
- 函数function
- 函数调用
- 可在交互环境下定义函数
- 可将函数放在脚本文件中作为它的一部分
- 可放在只包含函数的单独文件中
- 使用函数文件
SHELL脚本
1.脚本调试
检测脚本中的语法错误
bash -n /script
调试执行
bash -x /script
2.变量
- 不能使程序中的保留字:如:if, for
- 只能使用数字、字母及下划线,且不能以数字开头,注意:不支持短横线 “ – ”
- 见名知义,用英文名字,并体现出实际作用
- 统一命名规则:驼峰命名法, studentname,大驼峰StudentName 小驼峰studentName
- 变量名大写
- 局部变量小写
- 函数名小写
2.1变量赋值:
name=\'value\'
value可以是以下多种形式
直接字串:name=\'root\'变量引用:name=\"$USER\"命令引用:name=`COMMAND` 或者 name=$(COMMAND)
变量引用:
$name${name}
2.2显示已定义的所有变量:
set
2.3 删除变量
unset name
变量声明和赋值
export name =VALUE
declaree -x name =VALUE
指定变量为环境变量,可供shell以外的程序使用
显示所有环境变量:
envprintenvexportdeclare -x
只读变量
只读变量:只能声明定义,但后续不能修改和删除
声明只读变量:
readonly namedeclare -r name
查看只读变量:
readonly [-p]declare -r
位置变量(重点)
位置变量:在bash shell中内置的变量, 在脚本代码中调用通过命令行传递给脚本的参数
$1, $2, ... 对应第1个、第2个等参数,shift [n]换位置$0 命令本身,包括路径$* 传递给脚本的所有参数,全部参数合为一个字符串$@ 传递给脚本的所有参数,每个参数为独立字符串$# 传递给脚本的参数的个数注意:$@ $* 只在被双引号包起来的时候才会有差异
清空所有位置变量
set –
退出状态码变量
进程执行后,将使用变量 ?保存状态码的相关数字,不同的值反应成功或失败,? 保存状态码的相关数字,不同的值反应成功或失败,?保存状态码的相关数字,不同的值反应成功或失败,?取值范例 0-255
$?的值为0 代表成功$?的值是1到255 代表失败
范例:
ping -c1 -W1 hostdown &> /dev/nullecho $?
- 脚本中一旦遇到exit命令,脚本会立即终止;终止退出状态取决于exit命令后面的数字
- 如果未给脚本指定退出状态码,整个脚本的退出状态码取决于脚本中执行的最后一条命令的状态码
展开命令执行顺序
- 把命令行分成单个命令词
- 展开别名
- 展开大括号的声明({})
- 展开波浪符声明(~)
- 命令替换$() 和 “
- 再次把命令行分成命令词
- 展开文件通配(*、?、[abc]等等)
- 准备I/0重导向(<、>)
- 运行命令
防止拓展
反斜线(\\)会使随后的字符按原意解释
范例:
echo Your cost: \\$5.00Your cost: $5.00
加引号来防止扩展
- 单引号(’’)防止所有扩展
- 双引号(”“)也可防止扩展,但是以下情况例外:$(美元符号)
变量拓展
- “ : 反引号,命令替换
- \\:反斜线,禁止单个字符扩展
- !:叹号,历史命令替换
脚本安全和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
格式化输出printf
printf \"指定的格式\" \"文本1\" \"文本2\" ...
常用格式替换符
替换符 | 功能 |
---|---|
%s(string) | 字符串 |
%f(ffloat) | 浮点格式 |
%b | 相对应的参数中包含转义字符时,可以使用此替换符进行替换,对应的转义字符会被转义 |
%c | ASCII字符,即显示对应参数的第一个字符 |
%d,%i | 十进制整数 |
%o | 八进制值 |
%u | 不带正负号的十进制值 |
%x | 十六进制值(a-f) |
%X | 十六进制值(A-F) |
%% | 表示%本身 |
说明:%s 中的数字代表此替换符中的输出字符宽度,不足补空格,默认是右对齐,%-10s表示10个字
符宽,- 表示左对齐
#常用转义字符
转义符 | 功能 |
---|---|
\\a | 警告字符,通常为ASCII的BEL字符 |
\\b | 后退 |
\\f | 换页 |
\\n | 换行 |
\\c | 压缩换行 |
\\r | 回车 |
\\t | 水平制表符 |
\\v | 垂直制表符 |
\\ | 表示\\本身 |
范例:
printf \"%s\\n\" 1 2 3 41234
printf \"%f\\n\" 1 2 3 41.0000002.0000003.0000004.000000printf \"%f.2\\n\"1 2 3 41.002.003.004.00
printf \"(%s)\" 1 2 3 4(1)(2)(3)(4)printf\"%s %s\\n\"1 2 3 41 23 4
#%-10s 表示宽度10个字符,左对齐
printf \"%-10s %-10s %-4s %s \\n\" 姓名 性别 年龄 体重 小明 男 20 70小红 女 18 50姓名 性别 年龄 体重小明 男 20 70小红 女 18 50
算术运算
bash中的算术运算
+, -, *, /, %取模(取余), **(乘方)
乘法符号有些场景中需要转义
实现算术运算:
- let var=算术表达式
- var=$[算术表达式]
- var=$((算术表达式))
- var=$(expr arg1 arg2 arg3 …)
- declare –i var = 数值
- echo ‘算术表达式’ | bc
内建的随机数生成器变量:
$RANDOM 取值范围:0-32767
范例:
生成 0 – 49 之间随机数
echo $[$RANDOM%50]
随机字体颜色:
echo -en \” \\e[1;[[[RANDOM%7+31]\”
echo -en \” \\e[0m\”
增强型赋值:
+= i+=10 相当于 i=i+10(相当于i+10后重新赋值给自己)
-= i-=j 相当于 i=i-j
*=
/=
%=
++ i++,++i 相当于 i=i+1
– i–,–i 相当于 i=i-1
穿插内容:i++和++i的区别:
i++ :先赋值然后再加++i :先加然后赋值
格式:
let varOPERvalue
范例:
#自加3后自赋值let count+=3[root@centos8 ~]#i=10[root@centos8 ~]#let i+=20[root@centos8 ~]#echo $i30[root@centos8 ~]#j=20[root@centos8 ~]#let i*=j[root@centos8 ~]#echo $i600
范例:
自增,自减
let var+=1let var++let var-=1let var --
#重置变量
unset
i=1;let j=i++;echo \"i=$i,j=$j\"i=2 j=1i=1;let j=++i;echo \"i=$i,j=$j\"i=2 j=2
逻辑运算
true,false
1,0
与:&
- 1 与 1 = 1
- 1 与 0 = 0
- 0 与 1 = 0
- 0 与 0 = 0
或:|
- 1 或 1 = 1
- 1 或 0 = 1
- 0 或 1 = 1
- 0 或 0 = 0
非:! - ! 1 = 0 ! true
- ! 0 = 1 ! false
异或:^
异或的两个值,相同为假,不同为真(意思是肯定有一个是假的)
穿插异或运算
当两个值相同时,则为假,做减法运算,输出结果为0,;当两个值不同时,则为真,做加法运算,输出两数之和。
异或二进制
范例:
x=10;y=20;x=$[x^y];y=$[x^y];x=$[x^y];echo x=$x,y=$y x=20,y=10
条件测试命令
test或者 [ ] 或者[[ ]]
[ -v NAME ] 变量是否定义
[ -R NAME] 变量是否定义并且是名称引用
[ -n NAME ] 变量是否不为空
[ -z NAME ] 变量是否都为空
用法:
== 左侧字符串是否能够被右边的通配符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 是否存在且拥有GUID
-k FILE 是否存在且拥有sticky
文件属性测试
-s FILE 是否存在且非空
-t fd fd文件描述是否存在在某终端已经打开
-N FILE 文件自从上一次被打开后是否被修改过
-O FILE 当前有效用户是否为文件属主
-G FILE 当前有效用户是否为文件属组
FILE1 -ef FILE2 文件1是否是文件2的硬链接
FILE1 -nt FILE2 文件1是否新于文件2
FILE1 -ot FILE2 文件1是否旧于文件2
()和{}
都可将多个命令组合到一起批量执行
()在子进程中进行,不会影响当前环境
{}在当前shell中进行,影响单签环境
组合测试条件
[ expressio1 -a expressio2 ]并且,expressio和expressio2都是真,结果才是真,
[ expressio1 -o expressio2 ]或者expressio1和expressio2只要有一个是真,为真
[! expressio] 取反
(-a和-o需要使用测试命令进行,[[ ]]不支持)
eg:
ll /etc/data/scripts/disk.sh-rw-r--r--. 1 root root 349 Aug 8 10:03 disk.sh[ -f /data/scripts/disk.sh -a -o /data/scripts/disk.sh ];echo $?1[ -f /data/scripts/disk.sh -a -O /data/scripts/disk.sh ];echo $?
通配符eg:
FILE=\"a\";echo $FILEa[[ $FILE == a* ]];echo $?0---------------------------------FILE=\"ba\";echo $FILEba[[ $FILE == a* ]];echo $?1
([[ ]]中如果不想使用配配符*,只想表达*本身,可以用“”引起来,也可用\\)
正则表达式eg:
合理的考试成绩0-100
数值测试
大:greater
小:less
于:than
等:equal
-eq 是否等于
-ne是否不等于
-gt 是否大于
-ge 是否大于等于
-lt 是否小于
-le 是否小于等于
算术表达式比较:
== 相等
!= 不相等
<=
>=
<=
>
<
短路或取反
CMD1 && CMD2 对了继续,错了终止
CMD1 || CMD2 对了终止,错了继续
(并且正常逻辑是&&和||混合使用,&&要在前,||放在后面)
使用read命令来接受输入
read [options] [name]
eg:
read NAME AGE HIGHTgaga 22 180echo $NAME $AGE $HIGHTgaga 22 180
参数:
-p 指定要显示的提示
-s 静默输入,一般是口令输入
-n -N 指定输入的字符长度
-d ‘字符’ 输入结束符
-t N TIMEOUT为N秒
接受重定向:12345>abc,a=1,b=2,c=345
环境配置文件
1.分类
按照生效范围可分为两类
全局:
/etc/profile
/etc/profile.d/*.sh
/etc/bashrc
个人:
~/.nash_profile
~/.bashrc
2.功能分类
profile类为交互式登录的shell提供配置,常规用于定义环境变量,运行命令或脚本
bashrc类为非交互式登录的shell提供配置,常规用于定义别命名和函数,定义本地变量
3.Bash退出任务
保存在~/.bash_logout文件中(用户),在退出登录时运行。
- 创建自动备份
- 清除临时文件
4.流量控制,if,else语句
format:
单分支:
i
ifthenfi
双分支:
ifthenelsefi
多分支:
ifthenelifthenelifthen...elsefi
5.条件判断case语句
format:
case $NAME inpattern)CMD;;pattern)CMD;;esac
case支持glob风格的通配符
-
任意长度字符
? 任意单个字符
[ ] 指定范围内的任意单个字符
| 或者,如:a|b
6.循环
循环次数已知
循环次数未知
format:
1.
for A in list;do循环体done
for A in list
do
循环体
done
(page63)
格式2
双小括号方法即((…))格式,也可以用于算术运算,双小括号方法也可以使用bash shell实现c语言风格的变量操作
i=10;((i++))
for((:for((exp1;exp2;exp3)));docmddone
for((控制变量初始化;条件判断表达式;控制变量的修正表达式))do循环体done
- 控制变量初始化,仅在运行到循环代码段时执行一次
- 控制变量的修正表达式:每轮循环结束都会先进性控制变量的修正运算,而后在做条件判断。
while循环
格式:while CMD;do;CMD;done
while condition;do 循环体;done
dondition:循环控制条条件:进行入循环前先做一次判断;
每一次循环之后会再次做判断,条件为true时,则执行一次循环,直到条件测试为false时终止循环。
until
格式:until cmd;do;cmd;done
until condition;do
循环体
done
进入条件:condition为false
退出条件:comdition为true
当值为false时,执行循环
循环控制语句continue
continue
:提前结束第N层的本轮循环,而进入一轮判断;最内层为第1层。
格式:
while condition ;do
cmd1
…
if condition2;then
conitue
fi
cmdn
…
done
continue是不执行满足条件判断这次循环
循环控制语句break
break是直接中断后面所有循环。
外循环(
内循环(
break n (当break值为1时,中断内循环)(当值为2时,内循环执行到满足场景时中断,外部只执行一次)
)
)
循环控制shift命令
shift用于将参量列表list左移指定次数,缺省为左移一次。
参量列表list一旦被移动最左端那个参数就从列表中删除。while循环遍历位置参量列表时,常用到while
while特殊用法:while read
while循环的特殊用法,遍历文件或文本的每一行
格式:
while read line ;do
循环体
done < /path/from/somefile
依次读取/path/from/somefile文件中的每一行,且将赋值给变量line。
循环与菜单select
格式:
select name [in words…;] do cmd;done
select variable in list ;do
循环体命令
done
说明:
select循环主要用于创建菜单,按数字顺序排列的菜单显示在标准的错误上,并显示PS3提示符号,等待用户输入。
函数function
函数function是由若干条shell组成的语句块,实现代码重用和模块化编程,它与shell程序形式上是相似的,不同的是它不是一个单独的进程,不能独立运行,而是shell程序的一部分。
#语法一:func_name(){...函数体...}#语法二:function func_name{...函数体...}#语法三:function func_name(){...函数体...}
查看函数
#查看当前已定义的函数名
declare -F
#查看当前已经定义的函数定义
declare -f
#查看指定当前已定义的函数名
declare -f func_name
#查看当前已定义的函数名定义
declare -F func_name
#删除函数
unset func_name
函数调用
函数的调用方式
可在交互环境下定义函数
可将函数放在脚本文件中作为它的一部分
可放在只包含函数的单独文件中
调用:函数只有被调用才会执行,通过给定函数名调用函数。
在交互式环境中可已使用函数
在脚本中定义及使用函数
函数在使用前必须定义,因此应将函数定义放在脚本开始部分,直至shell首次发现它之后才能使用,调用函数仅使用其函数名即可。
使用函数文件
可以将经常使用的函数存入一个单独的函数文件,然后将函数文件载入shell,再进行调用函数文件名可以任意取,但最好与相关任务有某种联系。例如,functions
一旦函数文件载入shell,就可以在命令行或脚本中调用函数。可以使用declare -f 或者set命令查看所有定义的函数,其输出列表包括已经载入的shell的所有函数。
若要改动函数,首先unset命令从shell中删除函数。改动完毕后,再重新载入此文件。
在shell脚本或交互式中调用函数,格式如下:
. /filename 或source filename
函数返回值
函数的执行结果返回值:
使用echo等命令进行输出
函数体中调用命令的输出结果
函数的退出状态码:
默认取决于函数中执行的最后一条命令的退出状态码
自定义退出状态码,其格式为:
return ,从函数中返回,用最后状态命令决定返回值
return 0 无错误返回
return 1-255 有错误返回
环境函数
类似于环境变量,也可以定义环境函数,使子进程也可以使用父进程定义的函数
定义环境函数:
export -f function_name
declare -xf function_name
查看环境函数:
export -f
declare -xf
函数参数
函数可以接受参数:
- 传递参数给函数:在函数名后面可以空白分隔给定参数列表即可,如testfunc arg1 arg2 …
- 在函数体中,可使用$1,$2,…调用这些参数;还可以使用$@$*$#等特殊变量
eg:
函数变量
变量作用域:
- 普通变量:只有在当前shell进程有效,为执行脚本会启动专用子进程;因此,本地变量的作用范围是当前shell脚本程序文件,包括脚本中的函数。
- 环境变量:当前shell和子shell有效
- 本地变量:函数的生命周期;函数结束时变量被自动销毁
注意:
- 如果函数中定义了普通变量,且名称和局部变量相同,则使用本地变量
- 在函数中使用本地变量的方法
local name=value
函数递归
函数递归:函数直接或间接调用自身,注意递归层数,可能会陷入死循环
fork炸弹
fork炸弹是一种恶意程序,他的内部是一个不断循环的递归程序,急速耗尽内存资源:
:(){:|:&};: