shell script
是利用shell
的功能所写的一个“程序”,这个程序是使用纯文本文件,将一些 shell 的语法与指令(含外部指令)写在里面, 搭配正则表达式、管线命令与数据流重导向等功能,以达到我们所想要的处理目的,shell script
用在系统管理上面是很好的一项工具,但是用在处理大量数值运算上, 就不够好了,因为Shell scripts
的速度较慢,且使用的CPU
资源较多,造成主机资源的分配不良。
1. 解释器
shell
是解释性语言,包括各种版本的解释器,bash、csh、zsh
等等,我们往往会在shell脚本的第一行使用#!bash
的形式告诉系统其后路径所指定的程序即是解释此脚本文件的Shell
解释器
2. 注释
I. 单行注释
以
#
开头,到行尾结束。
#单行注释
II. 多行注释
以
:<<EOF
开头,到EOF
结束。
:<<EOF
echo '这是多行注释'
echo '这是多行注释'
echo '这是多行注释'
EOF
3. 执行方式
I. 直接执行
使用绝对路径、相对路径、
bash、sh
等,会使用一个新的 bash 环境来执行脚本内的指令,程序完成后程序内各项变量或动作将会结束而不会传回到父程序中
II. source执行
source name.sh
会在父程序中执行的,因此各项动作都会在原本的bash
内生效
##3. 变量
Bash
中没有数据类型,bash
中的变量可以保存一个数字、一个字符、一个字符串等等。同时无需提前声明变量,给变量赋值会直接创建变量。
I. 变量命名原则
- 命名只能使用英文字母,数字和下划线,首个字符不能以数字开头。
- 中间不能有空格,可以使用下划线(_)。
- 不能使用标点符号。
- 不能使用
bash
里的关键字(可用help
命令查看保留关键字)。 - 变量等号两边不能有空格出现
II. 声明变量
访问变量的语法形式为:
${var}
和$var
。
- 变量名外面的花括号是可选的,加不加都行,加花括号是为了帮助解释器识别变量的边界,所以推荐加花括号。
- 使用
readonly
命令可以将变量定义为只读变量readonly var
- 使用
unset
命令可以删除变量:unset var
。变量被删除后不能再次使用。unset
命令不能删除只读变量。
4. 引号
shell 字符串可以用单引号
''
,也可以用双引号“”
,也可以不用引号。
I. 单引号
- 单引号里不识别变量
- 单引号里不能出现单独的单引号(使用转义符也不行),但可成对出现,作为字符串拼接使用。
II. 双引号的特点
- 双引号里识别变量
- 双引号里可以出现转义字符
5. 字符串
shell 字符串可以用单引号 ‘’,也可以用双引号 “”,也可以不用引号。shell同时支持数字(字符串)的运算,但是在定义的时候字符串构成必须为纯数字
I. 字符串截取
{var:num}
:截取var字符串从第num
个字符到末尾的所有字符(从0开始),若num为负,则倒序截取并且num
与冒号之间必须有空格或者()隔断${var:num1:num2}
,num1
是位置,num2
是长度(非负)
var=abcdef
echo ${var: -2} #ef
echo ${var:1-3} #ef
echo ${var:(-2)} #ef
II. 字符串长度
echo ${#var}长度
III. 字符串查找
expr index "${text}" ll
查找ll
字符串在${text}
中最初出现的位置,从1开始索引
IV. 字符串替换
- 单个替换:
${var/pattern/pattern}
,将var
字符串的第一个匹配的pattern
替换为另一个pattern
- 全部替换:
${var//pattern/pattern}
,将var
字符串中的所有能匹配的pattern
替换为另一个pattern
6. 数组
bash
只支持一维数组,数组下标从 0 开始,下标可以是整数或算术表达式,其值应大于或等于 0。
I. 创建数组
可以一次性创建数组并初始化所有元素或者用下标赋值的方式定义数组
array_name=(value1 ... valuen)
nums=([2]=2 [0]=0 [1]=1)
colors=(red yellow "dark blue")
II. 访问数组元素
- 访问数组的单个元素:
echo ${nums[1]}
# Output: 1
- 访问数组的所有元素:
echo ${colors[*]}
# Output: red yellow dark blue
echo ${colors[@]}
# Output: red yellow dark blue
III. 添加元素
重新定义数组并将之前的值获取到并赋给新数组
new_array=(one "${array[@]}" three)
IV. 删除元素
unset array[0]
7. 运算符
I. 算数运算符 (假定变量 x 为 10,变量 y 为 20)
运算符 | 说明 | 举例 |
---|---|---|
+ | 加法 | expr $x + $y 结果为 30。 |
- | 减法 | expr $x - $y 结果为 -10。 |
* | 乘法 | expr $x * $y 结果为 200。 |
/ | 除法 | expr $y / $x 结果为 2。 |
% | 取余 | expr $y % $x 结果为 0。 |
= | 赋值 | x=$y 将把变量 y 的值赋给 x。 |
== | 相等。用于比较两个数字,相同则返回 true。 | [ $x == $y ] 返回 false。 |
!= | 不相等。用于比较两个数字,不相同则返回 true。 | [ $x != $y ] 返回 true。 |
- 条件表达式要放在方括号之间,并且要有空格,例如:
[$x==$y]
是错误的,必须写成[ $x == $y ]
。
II. 关系运算符(假定变量 x 为 10,变量 y 为 20)
关系运算符只支持数字,不支持字符串,除非字符串的值是数字
运算符 | 说明 | 举例 |
---|---|---|
-eq | 检测两个数是否相等,相等返回 true。 | [$a -eq $b ]返回 false。 |
-ne | 检测两个数是否相等,不相等返回 true。 | [$a -ne $b ] 返回 true。 |
-gt | 检测左边的数是否大于右边的,如果是,则返回true。 |
[$a -gt $b ] 返回 false 。 |
-lt | 检测左边的数是否小于右边的,如果是,则返回true。 |
[ $a -lt $b ] 返回 true。 |
-ge | 检测左边的数是否大于等于右边的,如果是,则返回true。 |
[ $a -ge $b ] 返回 false。 |
-le | 检测左边的数是否小于等于右边的,如果是,则返回true。 |
[ $a -le $b ]返回 true。 |
III. 布尔运算符(假定变量 x 为 10,变量 y 为 20)
运算符 | 说明 | 举例 |
---|---|---|
! | 非运算,表达式为true 则返回 false,否则返回 true。 |
[ ! false ] 返回 true。 |
-o | 或运算,有一个表达式为 true 则返回 true。 |
[ $a -lt 20 -o $b -gt 100 ] 返回 true。 |
-a | 与运算,两个表达式都为 true 才返回 true。 |
[$a -lt 20 -a $b -gt 100 ] 返回 false。 |
IV. 逻辑运算符(假定变量 x 为 10,变量 y 为 20)
运算符 | 说明 | 举例 |
---|---|---|
&& | 逻辑的 AND | [[ ${x} -lt 100 && ${y} -gt 100 ]] 返回 false |
\|\| |
逻辑的 OR | [[${x} -lt 100 \|\| ${y} -gt 100 ]] 返回 true |
V. 字符串运算符(假定变量 a 为 “abc”,变量 b 为 “efg”:)
运算符 | 说明 | 举例 |
---|---|---|
= | 检测两个字符串是否相等,相等返回 true。 |
[ $a = $b ] 返回 false。 |
!= | 检测两个字符串是否相等,不相等返回 true。 |
[ $a != $b ] 返回 true。 |
-z | 检测字符串长度是否为 0,为 0 返回 true。 |
[ -z $a ] 返回 false。 |
-n | 检测字符串长度是否为 0,不为 0 返回 true。 |
[ -n $a ] 返回 true。 |
str | 检测字符串是否为空,不为空返回 true。 |
[ $a ] 返回 true。 |
VI. 文件测试运算符
操作符 | 说明 | 举例 |
---|---|---|
-b file | 检测文件是否是块设备文件,如果是,则返回 true。 | [ -b $file ] 返回 false。 |
-c file | 检测文件是否是字符设备文件,如果是,则返回 true。 | [ -c $file ] 返回 false。 |
-d file | 检测文件是否是目录,如果是,则返回 true。 | [ -d $file ] 返回 false。 |
-f file | 检测文件是否是普通文件(既不是目录,也不是设备文件),如果是,则返回 true。 | [ -f $file ] 返回 true。 |
-g file | 检测文件是否设置了 SGID 位,如果是,则返回 true。 | [ -g $file ] 返回 false。 |
-k file | 检测文件是否设置了粘着位(Sticky Bit),如果是,则返回 true。 | [ -k $file ]返回 false。 |
-p file | 检测文件是否是有名管道,如果是,则返回 true。 | [ -p $file ] 返回 false。 |
-u file | 检测文件是否设置了 SUID 位,如果是,则返回 true。 | [ -u $file ] 返回 false。 |
-r file | 检测文件是否可读,如果是,则返回 true。 | [ -r $file ] 返回 true。 |
-w file | 检测文件是否可写,如果是,则返回 true。 | [ -w $file ] 返回 true。 |
-x file | 检测文件是否可执行,如果是,则返回 true。 | [ -x $file ] 返回 true。 |
-s file | 检测文件是否为空(文件大小是否大于 0),不为空返回 true。 | [ -s $file ] 返回 true。 |
-e file | 检测文件(包括目录)是否存在,如果是,则返回 true。 | [ -e $file ] 返回 true。 |
8. 条件流程控制
I. If … else
流程控制不可为空
if [ 条件判断式一 ]; then
当条件判断式一成立时,可以进行的指令工作内容;
elif [ 条件判断式二 ]; then
当条件判断式二成立时,可以进行的指令工作内容;
else
当条件判断式一与二均不成立时,可以进行的指令工作内容;
fi
- if语句的
[]
内注意前后空格[ 判断 ]
II. case … esac
case $变量名称 in #==关键字为 case ,还有变量前有钱字号
"第一个变量内容") #==每个变量内容建议用双引号括起来,关键字则为小括号 )
程序段
;; #==每个类别结尾使用两个连续的分号来处理!
"第二个变量内容")
程序段
;;
*) #==最后一个变量内容都会用 * 来代表所有其他值
不包含第一个变量内容与第二个变量内容的其他程序执行段
exit 1
;;
esac #==最终的 case 结尾!“反过来写”思考一下!
- 每个选项以
;;
结尾 - 用*)涵盖所有其他选项
exec
case ${oper} in
"+")
val=`expr ${x} + ${y}`
echo "${x} + ${y} = ${val}"
;;
"-")
val=`expr ${x} - ${y}`
echo "${x} - ${y} = ${val}"
;;
"*")
val=`expr ${x} \* ${y}`
echo "${x} * ${y} = ${val}"
;;
"/")
val=`expr ${x} / ${y}`
echo "${x} / ${y} = ${val}"
;;
*)
echo "Unknown oper!"
;;
esac
9. 循环
Bash 中有四种循环:for,while,until和select。
I. 不定循环
不定循环中有while和until两种,
while
中当condition
成立则进行循环,直到不成立,但until
刚好相反,在until
中当condition
成立时则终止循环,否则持续循环
while
while [ condition ] #中括号内的状态就是判断式
do #do 是循环的开始!
程序段落
done #done 是循环的结束
until
until [ condition ]
do
程序段落
done
II. 固定循环
for … in
for var in con1 con2 con3 ...
do
程序段
done
for(())
for (( 初始值; 限制值; 执行步阶 ))
do
程序段
done
这种语法适合于数值方式的运算当中,在
for
后面的括号内的三串内容意义为:
- 初始值:某个变量在循环当中的起始值,直接以类似
i=1
设置好; - 限制值:当变量的值在这个限制值的范围内,就继续进行循环。例如
i<=100;
- 执行步阶:每作一次循环时,变量的变化量。例如
i=i+1。
select循环
select
循环帮助我们组织一个用户菜单。它的语法几乎跟for
循环一致:
select answer in elem1 elem2 ... elemN
do
### 语句
done
select
会打印elem1..elemN
以及它们的序列号到屏幕上,之后会提示用户输入。通常看到的是$?(PS3变量)
。用户的选择结果会被保存到answe
r中。如果answer
是一个在1..N
之间的数字,那么语句会被执行,紧接着会进行下一次迭代 —— 如果不想这样的话我们可以使用break
语句。
#!/usr/bin/env bash
PS3="Choose the package manager: "
select ITEM in bower npm gem pip
do
echo -n "Enter the package name: " && read PACKAGE
case ${ITEM} in
bower) bower install ${PACKAGE} ;;
npm) npm install ${PACKAGE} ;;
gem) gem install ${PACKAGE} ;;
pip) pip install ${PACKAGE} ;;
esac
break # 避免无限循环
done
10. 函数
使用函数可以简化代码,方便调用
function fname() {
程序段
}
shell script
的执行方式是由上而下,由左而右, 因此在shell script
当中的function
的设置一定要在程序的最前面, 这样才能够在执行时被找到可用的程序段- 方法返回值的处理有三种方式,
return
返回(仅支持数值结果码,最大256)、echo
返回和保存于全局变量
11. Debug
[dmtsai@study ~]$ sh [-nvx] scripts.sh
选项与参数:
-n :不要执行 script,仅查询语法的问题;
-v :再执行 sccript 前,先将 scripts 的内容输出到屏幕上;
-x :将使用到的 script 内容显示到屏幕上,这是很有用的参数!
12. 参数
I. 位置参数
位置参数是在调用一个函数并传给它参数时创建的变量
变量 | 描述 |
---|---|
$0 |
脚本名称 |
$1 … $9 |
第 1 个到第 9 个参数列表 |
${10} … ${N} |
第 10 个到 N 个参数列表 |
$* or $@ |
除了$0外的所有位置参数 |
$# |
不包括$0在内的位置参数的个数 |
$FUNCNAME |
函数名称(仅在函数内部有值) |
II. 函数处理参数
参数处理 | 说明 |
---|---|
$# |
返回参数个数 |
$* |
返回所有参数 |
$$ |
脚本运行的当前进程 ID 号 |
$! |
后台运行的最后一个进程的 ID 号 |
$@ |
返回所有参数 |
$- |
返回 Shell 使用的当前选项,与 set 命令功能相同。 |
$? |
函数返回值 |