文章目录
一、形式因:什么是shell scripts?
- shell script 是利用 shell 的功能所写的一个“程序 ( program)”, 这个程序是使用纯文本文件, 将一些 shell 的语法与指令( 含外部指令) 写在里面, 搭配正则表达式、 管线命令与数据流重导向等功能, 以达到我们所想要的处理目的。而且 shell script 更提供阵列、 循环、 条件与逻辑判断等重要功能, 让使用者也可以直接以 shell 来撰写程序, 而不必使用类似 C 程序语言等传统程序。
二、目的因:为什么要学习shell scripts?
- 自动化管理:
管理服务器非常繁琐, 每天的任务有: 查询登录文件、 追踪流量、 监控使用者使用主机状态、 主机各项硬件设备状态、 主机软件更新查询、 以及要应付其他使用者的突然要求。 而这些工作可以写个简单的程序来每日“自动处理分析”,这就得要良好的 shell script 编写能力了。
- 入侵侦测功能:
当系统有异状时, 大多会将这些异状记录在系统记录器, 也就是常提到的“系统登录文件”,分析系统登录文件, 若察觉有问题, 立刻通报管理员, 或者是立刻加强防火墙的设置规则, 如此一来, 主机可就能够达到“自我保护”的聪明学习功能。 举例来说, 我们可以通过 shell script 去分析“当该封包尝试几次还是连线失败之后, 就予以抵挡住该 IP”之类的举动。
- 连续指令单一化:
script 最简单的功能就是: “汇整一些在 command line 下达的连续指令, 将他写入 scripts 当中, 而由直接执行 scripts 来启动一连串command line 指令输入! ”例如: 防火墙连续规则 ( iptables) , 开机载入程序的项目 ( 就是在 /etc/rc.d/rc.local里头的数据) 等等。scripts 可以想成”把一大串的指令汇整在一个文件里面, 直接执行该文件就可以执行那些繁琐的指令段“。
- 简易的数据处理:
awk 可以用来处理简单的数据数据, 例如薪资单的处理啊等等的。 shell script 的功能更强大,可以直接处理数据数据的比对, 文字数据的处理等等, 撰写方便, 速度又快。
- 跨平台支持与学习历程较短:
几乎所有的 Unix Like 都可以跑 shell script , 甚至 Windows 也有相关的 script 仿真器。
三、方法论:
(一)、shell script 撰写的注意事项:
- 指令的执行是从上而下、 从左而右的分析与执行;
- 指令的下达: 指令、 选项与参数间的多个空白都会被忽略掉;
- 空白行也将被忽略掉, 并且 [tab] 按键所推开的空白同样视为空白键;
- 如果读取到一个 Enter 符号 ( CR) , 就尝试开始执行该行 ( 或该串) 命令;
- 至于如果一行的内容太多, 则可以使用“ [Enter] ”来延伸至下一行;
- “ # ”可做为注解! 任何加在 # 后面的数据将全部被视为注解文字而被忽略!
(二)、脚本编程的好习惯:
建议你一定要养成良好的 script 撰写习惯, 在每个 script 的文件开始处记录好:
- script 的功能;
- script 的版本信息;
- script 的作者与联络方式;
- script 的版权宣告方式;
- script 的 History ( 历史纪录) ;
- script 内较特殊的指令, 使用“绝对路径”的方式来下达;
- script 运行时需要的环境变量预先宣告与设置;
- 加上注解;
- 利用缩进是程序更美观的同时更容易查找bug。
(三)、shell scripts如何执行?
前提: shell.sh 文件必须要具备可读与可执行 ( rx) 的权限
1、直接指令下达:
- 绝对路径: 使用 /home/dmtsai/shell.sh 下达指令;
- 相对路径:假设工作目录在 /home/dmtsai/ , 使用 ./shell.sh 来执行
- 变量“PATH”功能: 将 shell.sh 放在 PATH 指定的目录内, 例如: ~/bin/
2、以 bash 程序来执行:
- 通过“ bash shell.sh ”或“ sh shell.sh ”来执行
(四)、script 执行方式差异 ( source, sh script, ./script)
1、 利用直接执行的方式来执行 script
使用直接指令下达 ( 不论是绝对路径/相对路径还是 ${PATH} 内) , 或者是利用 bash ( 或 sh) 来下达脚本时, 该 script 都会使用一个新的 bash 环境来执行脚本内的指令, 也就是说, 使用这种执行方式时, 其实 script 是在子程序的 bash 内执行的!
2、 利用 source 来执行脚本:
在父程序中执行。
四、质料因:
(一)、变量
1、什么是变量(形式因)?
变量命名法则:
1、不能使程序中的保留字:例如if, for;
2、只能使用数字、字母及下划线,且不能以数字开头;
3、见名知义,
2、变量的作用(目的因)?
3、变量有哪些类型(质料因)?
(1)、根据变量的生效范围等标准:
本地变量:生效范围为当前shell进程;对当前shell之外的其它shell进程,包括当前shell的子shell进程均无效;
环境变量:生效范围为当前shell进程及其子进程;
局部变量:生效范围为当前shell进程中某代码片断(通常指函数);
位置变量:$1、
2
,
.
.
.
来
表
示
,
用
于
让
脚
本
在
脚
本
代
码
中
调
用
通
过
命
令
行
传
递
给
它
的
参
数
;
特
殊
变
量
:
2, ...来表示,用于让脚本在脚本代码中调用通过命令行传递给它的参数; 特殊变量:
2,...来表示,用于让脚本在脚本代码中调用通过命令行传递给它的参数;特殊变量:?, $0, $*, $@, $#
a、本地变量:
-
变量赋值:name=‘value’
value: -
可以是直接字串; name=“username”
-
变量引用:name="$username"
-
命令引用:name=
COMMAND
,或者 name=$(COMMAND) -
变量引用:${name}, $name
-
“”:弱引用,其中的变量引用会被替换为变量值;
-
‘’:强引用,其中的变量引用不会被替换为变量值,而保持原字符串;
-
显示已定义的所有变量
set -
销毁变量:
unset name
b、环境变量:
-
变量声明、赋值:
export name=VALUE
declare -x name=VALUE -
变量引用:$name, ${name}
-
显示所有环境变量:
export
env
printenv -
销毁:
unset name -
bash有许多内建的环境变量:
PATH, SHELL, UID, HISTSIZE, HOME, PWD, OLD, HISTFILE, PS1
c、只读变量:
- 变量声明
readonly name
declare -r name
d、位置变量:
在脚本代码中调用通过命令行传递给脚本的参数:
- $1, $2, … :对应调用第1、第2等参数;
- $0: 命令本身;
- $* : 传递给脚本的所有参数;
- $@ : 传递给脚本的所有参数;
- $# : 传递给脚本的参数的个数;
(二)、数组(array):
1、 什么是数组(形式因)?
变量是存储单个元素的内存空间,数组是存储多个元素的连续的内存空间,可以说数组是变量的集合。
2、数组由什么组成(质料因)?
数组由数组名和索引组成;
-
数组名:
和变量名命名规则一样。 -
索引:
-
编号从0开始,属于数值索引;
-
索引也可支持使用自定义的格式,而不仅仅是数值格式;bash的数组支持稀疏格式;
3、如何使用数组(方法论)?
(1)、声明数组:
declare -a ARRAY_NAME
declare -A ARRAY_NAME: 关联数组;
(2)、数组元素的赋值:
- 一次只赋值一个元素;
ARRAY_NAME[INDEX]=VALUE
weekdays[0]=“Sunday”
weekdays[4]=“Thursday”
- 一次赋值全部元素:
ARRAY_NAME=(“VAL1” “VAL2” “VAL3” …)
- [ ]只赋值特定元素:
ARRAY_NAME=([0]=“VAL1” [3]=“VAL2” …)
- read -a ARRAY
(3)、引用数组中的元素:
-
引用数组中的单个元素:${ARRAY_NAME[INDEX]}
注意:省略[INDEX]表示引用下标为0的元素; -
引用数组中的所有元素:${ARRAY[@]}, ${ARRAY[*]}
-
数组切片:${ARRAY[@]:offset:number}
-
offset: 要跳过的元素个数;
-
number: 要取出的元素个数,取偏移量之后的所有元素:${ARRAY[@]:offset};
(4)、数组的长度(数组中元素的个数):${#ARRAY_NAME[*]}, ${#ARRAY_NAME[@]}
(5)、向数组中追加元素:ARRAY[${#ARRAY[*]}]
(6)、删除数组中的某元素:
unset ARRAY[INDEX]
(7)、关联数组:
假如是下标是数值,从0递增的话就是索引数组,但是我们下标不想用数字的时候,想用星期一、
星期二等怎么办?那么这时候就要用关联数组了;
declare -A ARRAY_NAME
ARRAY_NAME=([index_name1]=‘val1’ [index_name2]=‘val2’ …)
(三)、条件测试:
1、条件测试的目的(目的因)?
判断某需求是否满足,需要由测试机制来实现;
Note: 专用的测试表达式需要由测试命令辅助完成测试过程;
2、条件测试由什么组成(质料因)?
(1)、测试命令:
-
test EXPRESSION
-
[ EXPRESSION ]
使用中括号必须要特别注意, 因为中括号用在很多地方, 包括万用字符与正则表达式等等,所以如果要在 bash 的语法当中使用中括号作为 shell 的判断式时, 必须要注意中括号的两端需要有空白字符来分隔喔!
注意: -
在中括号 [] 内的每个元件都需要有空白键来分隔;
-
在中括号内的变量, 最好都以双引号括号起来;
-
在中括号内的常数, 最好都以单或双引号括号起来。
-
[[ EXPRESSION ]]
注意: EXPRESSION前后必须有空白字符;
(2)、测试类型:
a、 数值测试:
- -gt: 是否大于;
- -ge: 是否大于等于;
- -eq: 是否等于;
- -ne: 是否不等于;
- -lt: 是否小于;
- -le: 是否小于等于;
b、 字符串测试:
注意:用于字符串比较时的用到的操作数都应该使用引号;
- ==:是否等于;
- >: 是否大于;
- <: 是否小于;
- !=: 是否不等于;
- =~: 左侧字符串是否能够被右侧的PATTERN所匹配;
注意: 此表达式一般用于[[ ]]中; - -z “STRING”:测试字符串是否为空,空则为真,不空则为假;
- -n “STRING”:测试字符串是否不空,不空则为真,空则为假;
c、文件测试:
(a)、存在性测试
- -a FILE
- -e FILE: 文件存在性测试,存在为真,否则为假;
(b)、存在性及类别测试
- -b FILE:是否存在且为块设备文件;
- -c FILE:是否存在且为字符设备文件;
- -d FILE:是否存在且为目录文件;
- -f FILE:是否存在且为普通文件;
- -h FILE 或 -L FILE:存在且为符号链接文件;
- -p FILE:是否存在且为命名管道文件;
- -S FILE:是否存在且为套接字文件;
(c)、文件权限测试:
- -r FILE:是否存在且可读;
- -w FILE: 是否存在且可写;
- -x FILE: 是否存在且可执行;
(d)、文件特殊权限测试:
- -g FILE:是否存在且拥有sgid权限;
- -u FILE:是否存在且拥有suid权限;
- -k FILE:是否存在且拥有sticky权限;
(e)、文件大小测试:
- -s FILE: 是否存且非空;
(f)、文件是否打开:
- -t fd: fd表示文件描述符是否已经打开且与某终端相关
- -N FILE:文件自动上一次被读取之后是否被修改过;
- -O FILE:当前有效用户是否为文件属主;
- -G FILE:当前有效用户是否为文件属组;
(g)、双目测试:
- FILE1 -ef FILE2: FILE1与FILE2是否指向同一个设备上的相同inode
- FILE1 -nt FILE2: FILE1是否新于FILE2;
- FILE1 -ot FILE2: FILE1是否旧于FILE2;
d、组合测试:
(a)、逻辑运算:
- 第一种方式:
- COMMAND1 && COMMAND2
- COMMAND1 || COMMAND2
- ! COMMAND
例如:[ -e FILE ] && [ -r FILE ]
- 第二种方式:
- EXPRESSION1 -a EXPRESSION2
- EXPRESSION1 -o EXPRESSION2
- ! EXPRESSION
(四)、算术运算:
1、什么是算术运算(形式因)?
数值的+, -, *, /, %, ** 等运算
2、实现算术运算有哪些方式(方法论)?
(1)、实现算术运算:
- let var=算术表达式
- var=$[算术表达式]
- var=$((算术表达式))—推荐
- var=$(expr arg1 arg2 arg3 …)
乘法符号有些场景中需要转义;
(2)、增强型赋值:
- +=
- -=
- *=
- /=
- %=
例如:let count+=1
(3)、自增,自减:
- let var+=1
- let var++
- let var-=1
- let var–
(五)、条件判断式:
1、利用if…then:
(1)、单层、简单条件判断:
if [判断条件]; then
条件为真的分支代码
fi
- 有多个条件要判别时, 除了可以将多个条件写入一个中括号内的情况之外, 还可以有多个中括号来隔开,而括号与括号之间, 则以 && 或 || 来隔开, 他们的意义是:
- && 代表 AND ;
- || 代表 or ;
(2)、多重、复杂条件判断:
if [判断条件]; then
条件为真的分支代码
else
条件为假的分支代码
fi
或者:
if [ 判断条件一 ]; then
条件一为真的分支代码
elif [ 判断条件二 ];then
条件一为真的分支代码
else
条件为假的分支代码
fi
2、利用case…esac判断:
case 变量引用 in
PAT1)
分支1
;;
PAT2)
分支2
;;
...
*)
默认分支
;;
esac
(六)、循环:
1、循环是什么(形式因)?
循环可以将某代码段重复运行多次, 直到使用者设置的条件达成为止。 重点是那个“条件的达成”是什么。 除了这种依据判断式达成与否的不定循环之外, 还有另外一种已经固定要跑多少次的循环形态, 可称为固定循环的形态。
2、循环有哪些类型(质料因)?
根据循环次数事先已知或未知可分为不定循环(必须有进入条件和退出条件:)和固定循环:
(1)、 不定循环:
a、 while … do … done循环:
while [ CONDITION ]; do
循环体
done
#CONDITION是循环控制条件(CONDTION一般有循环控制变量,并且循环控制变量的值会在循环体不断地被修正):
1. 进入循环之前,先做一次判断;
2. 每一次循环之后会再次做判断;
3. 当CONDITION循环控制条件为“true”,则执行一次循环;
4. 直到条件测试状态为“false”终止循环;
(a)、循环控制语句(用于循环体中):
- continue [N]:提前结束第N层的本轮循环,而直接进入下一轮判断;
while CONDTIITON1; do
CMD1
...
if CONDITION2; then
continue
fi
CMDn
...
done
- break [N]:提前结束循环;
while CONDTIITON1; do
CMD1
...
if CONDITION2; then
break
fi
CMDn
...
done
b、until … do… done 循环:
until [ CONDITION ]
do
循环体
done
(2)、固定循环:
a、for … do…done 循环:
相对于 while,until 的循环方式是必须要“符合某个条件”的状态,for循环的语法是“已经知道要进行几次循环的状态”
for var in con1 con2 con3 ...
do
程序段
done
第一次循环时, $var 的内容为 con1 ;
第二次循环时, $var 的内容为 con2 ;
第三次循环时, $var 的内容为 con3 ;
...
con1 con2 con3 ...列表生成方式:
1、整数列表
{start..end}
2、$(seq start [[step]end])
b、for … do…done 的数值处理循环:
for ((初始值;限制值;执行步阶))
do
程序段
done
初始值: 某个变量在循环当中的起始值, 直接以类似 i=1 设置好;
限制值: 当变量的值在这个限制值的范围内, 就继续进行循环。 例如i<=100;
执行步阶: 每作一次循环时, 变量的变化量。 例如 i=i+1。
(七)、函数(function):
1、函数的作用(目的因)?
代码重用;
2、函数的类型(质料因):
(1)、语法一:
function f_name {
…函数体…
}
(2)、语法二:
f_name() {
…函数体…
}
3、函数的使用(方法论)
(1) 调用:
给定函数名,函数名出现的地方,会被自动替换为函数代码;
(2)、 函数的生命周期:
被调用时创建,返回时终止;
(3)、return命令返回自定义状态结果:
- 0:成功
- 1-255:失败
(4)、函数返回值:
a、函数的执行结果返回值:
-
[ ]使用echo或print命令进行输出;
-
函数体中调用命令的执行结果;
b、函数的退出状态码:
- 默认取决于函数体中执行的最后一条命令的退出状态码;
- 自定义退出状态码:
return 0或1-255
(5)、函数可以接受参数:
a、传递参数给函数:
- 调用函数时,在函数名后面以空白分隔给定参数列表即可;例如“testfunc arg1 arg2 …”
b、在函数体中当中:
- 可使用$1, 2 , . . . 调 用 这 些 参 数 ; 还 可 以 使 用 2, ...调用这些参数;还可以使用 2,...调用这些参数;还可以使用@, $*, $#等特殊变量
(六)、函数中变量作用域:
-
本地变量:当前shell进程;为了执行脚本会启动专用的shell进程;因此,本地变量的作用范围是当前shell脚本程序文件;
-
局部变量:函数的生命周期;函数结束时变量被自动销毁;
在函数中定义局部变量的方法:local NAME=VALUE
(八)、shell script 的追踪与debug:
1、为什么需要追踪与debug(目的因)?
scripts 在执行之前, 最怕的就是出现语法错误的问题,所以需要进行调试找到问题并进行修正。
2、如何进行debug(方法论)?
以 bash 的相关参数来进行判断:
bash [options] [command_string | file]
选项:
-n:不要执行script,仅查询语法的问题;
-v:在执行script前,先将script的内容输出到屏幕上;
-x:将使用到的script内容显示到屏幕上;