shell脚本文件编写
1.shell脚本的基本格式
脚本文件的第一行必须是#! /bin/bash,后面再加上要执行的指令。在脚本文件中代码的注释使用#
#! /bin/sh
#这是第一个脚本文件
date #输出系统当前时间 格式如:2019年 12月 27日 星期五 23:28:07 CST
echo "hello world." #echo在终端输出内容
多行注释可以使用如下格式:
:<<EOF
注释内容...
注释内容...
注释内容...
EOF
上面代码中的EOF可以使用任意字符替换,比如a、b、c等,但是使用单引号和`两个。例如:
#!/bin/sh
#这里是单行注释,系统不会执行
:<<``
这里是多行注释,系统也不会执行
这里是多行注释,系统也不会执行
这里是多行注释,系统也不会执行
这里是多行注释,系统也不会执行
``
:<<''
这里也是多行注释,系统也不会执行
这里也是多行注释,系统也不会执行
这里也是多行注释,系统也不会执行
这里也是多行注释,系统也不会执行
''
:<<a
这里也是多行注释,系统也不会执行
这里也是多行注释,系统也不会执行
这里也是多行注释,系统也不会执行
这里也是多行注释,系统也不会执行
a
:<<#
这里也是多行注释,系统也不会执行
这里也是多行注释,系统也不会执行
这里也是多行注释,系统也不会执行
这里也是多行注释,系统也不会执行
#
2.shell/Linux的基本指令
Linux中基本指令实在是太多了,要想通过看一片博客就掌握是不可能的,还是得自己平时多多使用Linux,慢慢积累总结才能收获最多。如果你真的想系统学习一下Linux中的基本指令,这里推荐几个参考网站:
3.shell脚本基本语法
3.1 变量
变量的命名方式和c语言一样。使用一个定义过的变量只要在变量名前面加美元符号即可,变量名外面的花括号是可选的,加花括号是为了帮助解释器识别变量的边界。
脚本中的变量都是字符串,一个变量可以处于已定义和未定义两种状态,已定义的变量可以有值也可以为空。
定义变量时不要有空格,要获取一个变量的值的时候,用${}把变量名包起来,叫做变量扩展。扩展得到的变量可以在脚本中使用。
示例:
a='hello'
echo $a
结果为:
hello
注意:
切记,在定义变量的时候不要习惯性的在等号两边加空格,一旦加了空格,系统会把a识别成一个指令,会报系统找不到a指令的错误
例如:
a = hello
echo ${a}
结果为:
a:未找到命令
变量加不加引号都是一样的效果
在${var}后面还可以拼接其他字符串,相当于拼接字符串
例如:
a=hello echo ${a}_world结果为:
hello_world
用$()可以获取指令的输出当做变量
例如:
a=hello
#将echo输出的a的内容当做变量赋值给变量b
b=$(echo $a)
#变量输出的是a的内容
echo $b
结果为:
hello
可以将变量设置为只读,例如:
a=hello readonly a a=world系统会提示变量为只读,结果如下:
bash: a: 只读变量删除变量 unset
a=hello unset a echo a这里输出为空
字符串可以用单引号,也可以用双引号,可以不用引号。单双引号的区别为:
1.单引号里的任何字符都会原样输出,单引号字符串中的变量是无效的
2.单引号字符串中不能出现单独一个单引号(对单引号使用转义字符也不行),但可以成对出现,作为字符串拼接使用。
3.双引号里面可以有变量
4.双引号里面可以出现转义字符
例如
a="hello" #使用双引号拼接 b1="${a},world!" b2=""$a",world!" echo $b1 $b2 #使用单引号拼接 b3=''$a',world!' b4='${a},world!' echo $b3 $b4结果为:
hello,world! hello,world! hello,world! ${a},world!
3.2 数组
bash支持一维数组,但不支持多维数组,并且没有限定数组的大小。
类似于c语言,数组元素的下标由0开始编号。获取数组中的元素要利用下标,下标可以是整数或算术表达式,其值应大于或等于0.
3.2.1数组的定义
在shell中,用括号来表示数组,数组元素用“空格”符号分隔开,定义数组的一般形式为:
array_name=(value0 value1 value2 value3 value4)
或者
array_name=(
value0
value1
value2
value3
value4
)
还可以单独定义数组的各个元素
array_name[0]=value0
array_name[1]=value1
array_name[n]=valuen
可以使用不连续的下标,而且下标的范围没有限制。
示例:
3.2.2 读取数组
读取数组的一般形式为:
#读取数组中第n+1个元素
${array_name[n]}
使用@符号可以获取数组中的所有元素
echo ${array_name[@]}
3.2.3 获取数组的长度
获取数组的长度的方法与获取字符串长度的方法相同,格式为:
#获取数组元素的个数
length=${#array_name[@]}
#或者
length=${#array_name[*]}
#取得数组单个元素的长度
length=${#array_name[n]}
数组综合示例:
#定义数组
array=(1 2 3 4 5 6)
#单独定义数组元素,并使用不连续的数组下标
array[8]=7
#获取单个元素
echo ${array[8]}
#获取没有不存在的下标的值为空
echo ${array[6]}
#获取所有元素
echo ${array[@]}
#获取数组的长度
length=${#array[@]}
结果为:
7
1 2 3 4 5 6 7
7
3.3 条件赋值
v a r : − s t r i n g 和 {var:-string}和var:−string和{var:=string}
若变量
var为空或者没有定义,则用string作为${var:-string}的值,否则用变量var的展开值,但是虽然${var:-string}整体的值是string的值,但是变量var的值还是为空或者未定义,并没有改变变量var。${var:=string}就会改变变量var的值,只要var为空,就会把string的值赋值给var。例如:
#直接输出变量a的值,因为没有定义a变量,因此这里书出的内容应该为空 echo $a #使用${var:=string}格式 echo ${a:-hello} #这里输出的是hello,但是a变量仍然是为定义 #因此这里输出的a变量的值依然是空 echo $a #使用${var:=string}这个格式 echo ${a:=abc} #使用${var:=string}格式 echo ${a:-hello} #因为这里a变量已经有了值,因此直接使用a变量的值输出的应该是abc${var:+string}
规则和上面
${var:-string}完全相反,当var不是空的时候值为string,若为空,则是var的值。#直接使用${var:+string} echo ${a:abc} #a未定义,因此这里输出的是a的值 #给变量赋值 a=hello #再次使用${var:+string} echo ${a:abc} #因为a变量已经有了值,因此这里输出的是a的值${var:?string}
若变量
var的值不为空,则用变量var的值作为展开值,否则将string输出到标准错误中,并从脚本中退出#给变量赋初值 a=hello #使用${var:?string} echo ${a:?aab}#因为a有值,因此这里输出的应该是a的值hello #直接对变量b使用${var:?stirng} echo ${b:?abc}#这里b没有定义,值为空,因此输出应该是标准错误bash: b: abc$((C表达式)
这种计算是符合c语言的运算符,也就是只要符合c的运算符都可以用在上面表达式里面。
注意:
- 这种扩展计算是整数类型的计算,不支持浮点数核字符串。如果是逻辑运算,表达式为真则为1,否则为0
- 上面表达式必须是要用括号把表达式包起来
#给变量a赋初值10 a=10 #如果变量a大于0,输出1,否则输出0 echo $((a>0?1:0)) #这里输出应该为1模式匹配替换结构
表达式 功能 ${var%pattern} 从右边最短匹配 ${var%%pattern} 从右边最长匹配 ${var#pattern} 从左边最短匹配 ${var##pattern} 从左边最长匹配 注意:输出的内容是var去掉pattern匹配到的呢部分北荣剩下的内容
例如:
a=a.tar.gz #查找a中去掉满足匹配内容最长字符串后剩下的内容 #这里的输出应该为a.tar echo ${a##*.} echo ${a%%.*}6.字符串替换:v a r / s t r 1 / s t r 2 和 {var/str1/str2}和var/str1/str2和{var//str1/str2}
表达式 功能 ${var/str1/str2} 将var变量中的第一个str1子字符串替换为字符串str2 ${var//str1/str2} 将var变量中所有的str1子字符串全部替换为str2 示例:
a=a.tar.gz #将a字符串中的第一个'a'替换为'b' #输出应该为b.tar.gz echo ${a/a/b} #将字符串中所有'.'替换为'/' #输出应该为b/tar/gz echo ${a//.//}7.获取字符串长度:${#var}
示例:
a=abcdefghijklmn #获取a变量中字符串长度 echo ${#a}结果:
14
3.4 流程控制
3.4.1 if-then语句
if command1;then
commands
elif command2;then
commands
else
commands
fi
if语句会执行后面的命令,如果命令退出状态码是0,则是正常退出,位于then部分的命令就会执行,否则else部分的命令会执行。
参考博客:
3.5 Shell传递参数
我们可以在执行Shell脚本时,向脚本传递参数,脚本内获取参数的格式为:$n.n表示一个数字,1
为执行脚本的第一个参数,2为执行脚本的第二个参数,以此类推…
需要注意的是:$0为执行文件的文件名,是默认传递的
示例:
创建一个test.sh文件,文件内容为:
#! /bin/sh
echo "执行脚本文件为:$0";
echo "第一个参数为:$1";
echo "第二个参数为:$2";
echo "第三个参数为:$3";
在文件所在目录打开终端,并输入以下命令:
chmod +x test.sh
./test.sh hello world szq
结果为:
执行脚本文件为:./test.sh
第一个参数为:hello
第二个参数为:world
第三个参数为:szq
另外,还有几个特殊字符用来处理参数:
| 参数处理 | 说明 |
|---|---|
| $# | 参数的个数 |
| $* | 以一个单字符串显示所有向脚本传递的参数 |
| $$ | 脚本运行的当前进程的id号 |
| $! | 后台运行的最后一个进程的id号 |
| $@ | 与$*相同,但是使用时加引号,并在引号中返回每个参数 |
| $- | 显示Shell使用的当前选项,与set命令功能相同 |
| $? | 显示最后命令的退出状态,0表示没有错误,其他任何值表明有错误 |
示例
#! /bin/sh
echo "执行脚本文件为:$0";
echo "第一个参数为:$1";
echo "第二个参数为:$2";
echo "第三个参数为:$3";
echo "参数个数为:$#";
echo "传递的参数作为一个字符串显示:$*";
echo "脚本运行的当前进程的id号:$$";
echo "后台运行的最后一个进程的id号:$!";
echo "显示Shell使用的当前选项:$-";
echo "显示最后命令的退出状态,0表示没有错误,其他任何值表明有错误:$?";
结果为:
执行脚本文件为:./test.sh
第一个参数为:hello
第二个参数为:world
第三个参数为:szq
参数个数为:3
传递的参数作为一个字符串显示:hello world szq
脚本运行的当前进程的id号:6578
后台运行的最后一个进程的id号:
显示Shell使用的当前选项:
显示最后命令的退出状态,0表示没有错误,其他任何值表明有错误:0
∗ 与 *与∗与@的区别:
- 相同点:都是引用所有参数。
- 不同点:只有在双引号中体现出来。
$*相当于将输入的所有参数当成一个字符串处理,而$@将各个参数分开处理。
例如:
#!/bin/sh
echo 'test $*'
for ele in "$*";do
echo $ele;
done
echo 'test $@'
for ele in "$@";do
echo $ele;
done
执行下面命令:
chmod +x test.sh
./test.sh hello world szq
结果为:
test $*
hello world szq
test $@
hello
world
szq
3.6 基本运算符
3.6.1 算术运算符
下表列出了常用的算术运算符,假定变量a为10,变量b为20:
| 运算符 | 说明 | 举例 |
|---|---|---|
| + | 加法 | `expr $a + $b` 结果为30 |
| - | 减法 | `expr $a - $b` 结果为-10 |
| * | 乘法 | `expr $a * $b` 结果为200 |
| / | 出发 | `expr $b / $a` 结果为2 |
| % | 取余 | `expr $b % $a` 结果为0 |
| = | 赋值 | a = $b 将变量b的值赋值给a |
| == | 相等,用于比较两个数字是否相同,相同则返回true | [$a == $b] 返回false |
| != | 不相等,用于比较两个数字,不相同则返回true | [$a != $b] 返回true |
注意:
- 原生bash不支持简单的数学运算,但是可以通过其他命令来实现,例如awk和expr,expr最常用。
- 表达式和运算符之间要有空格,例如2+2是不对的,必须写成2 + 2
- 完整的表达式要被``包含,注意这个字符不是常用的单引号,在ESC下面
- 乘法前边必须加反斜杠才能实现乘法运算
例如
#!/bin/sh
a=10
b=20
#加法
val=`expr $a + $b`
echo "a + b = $val"
#减法
val=`expr $a - $b`
echo "a - b = $val"
#乘法
val=`expr $a \* $b`
echo "a * b = $val"
#除法
val=`expr $b / $a`
echo "b / a = $val"
#取余运算
val=`expr $b % $a`
echo "b % a = $val"
#赋值
a=$b
echo "a = $a"
#判断a和b是否相等,这里有可能出错,可以将==换成-eq
if [$a == $b]
then
echo "a等于b"
fi
#判断a和b是否不相等,这里也可能出错,可以将!=换成-ne
if [$a != $b]
then
echo "a不等于b"
fi
结果为:
a + b = 30
a - b = -10
a * b = 200
b / a = 2
b % a = 0
a = 20
a等于b
3.6.2 关系运算符
关系运算符只支持数字,不支持字符串,除非字符串的值为数字。
下表列出了常用的关系运算符,假定变量a的值为10,变量b为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 |
示例:
#!/bin/sh
a=10
b=20
#判断是否相等
if [ $a -eq $b ]
then
echo "a等于b"
fi
#判断是否不相等
if [ $a -ne $b ]
then
echo "a不等于b"
fi
#判断a是否大于b
if [ $a -gt $b ]
then
echo "a大于b"
fi
#判断a是否小于b
if [ $a -lt $b ]
then
echo "a小于b"
fi
#判断a是否大于等于b
if [ $a -ge $b ]
then
echo "a大于等于b"
fi
#判断a是否小于等于b
if [ $a -le $b ]
then
echo "a小于等于b"
fi
结果为:
a不等于b
a小于b
a小于等于b
注意:
- 在
[]内部开始和结尾都需要有空格,即[$a - eq $b]是不对的,只能[ $a -eq $b ]
3.6.3 布尔运算符
下表列出了常用的布尔运算符,假定变量a为10,变量b为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 |
示例
#!/bin/sh
a=10
b=20
#测试与运算
if [ $a -lt 100 -a $b -gt 15 ]
then
echo "a小于100并且b大于15,返回true"
else
echo "a小于100并且b大于15,返回false"
fi
#测试或运算
if [ $a -lt 100 -o $b -gt 100 ]
then
echo "a小于100或者b大于100:返回true"
else
echo "a小于100或者b大于100:返回false"
fi
#测试非运算
if [ ! $a -lt 100 ]
then
echo "a小于100的非运算:返回true"
else
echo "a小于100的非运算:返回false"
fi
结果为
a小于100并且b大于15,返回true
a小于100或者b大于100:返回true
a小于100的非运算:返回false
3.6.4 逻辑运算符
下表列出常用的逻辑运算符,假定变量a为10,变量b为20
| 运算符 | 说明 | 举例 |
|---|---|---|
| && | 逻辑的AND | [[ $a -lt 100 && $b -gt 100 ]] 返回false |
| || | 逻辑的OR | [[ $a -lt 100 || $b -gt 100 ]]返回true |
示例
#!/bin/sh
a=10
b=20
#测试逻辑与
if [[ $a -lt 100 && $b -gt 100 ]]
then
echo "a小于100并且b大于100:返回true"
else
echo "a小于100并且b大于100:返回false"
fi
#测试逻辑或
if [[ $a -lt 100 || $b -gt 100]]
then
echo "a小于100或者b大于100:返回true"
else
echo "a小于100或者b大于100:返回false"
fi
结果为
a小于100并且b大于100:返回false
a小于100或者b大于100:返回true
3.6.5 字符串运算符
下表列出了常用的字符串运算符,假定变量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 |
| $ | 检测字符串是否为空,不为空返回true | [ $a ] 返回true |
示例
#!/bin/sh
a=abc
b=efg
#测试两个字符串是否相等
if [ $a = $b ]
then
echo "字符串a等于字符串b"
else
echo "字符串a不等于字符串b"
fi
#测试两个字符串是否不相等
if [ $a != $b ]
then
echo "字符串a不等于字符串b"
else
echo "字符串a等于字符串b"
fi
#测试字符串长度是否为0
if [ -z $a ]
then
echo "字符串a的长度为0"
else
echo "字符串a的长度不为0"
fi
#测试字符串长度是否为0,第二种方法
if [ -n $a ]
then
echo "字符串a的长度不为0"
else
echo "字符串b的长度为0"
fi
#判断字符串是否为空
if [ $a ]
then
echo "字符串不为空"
else
echo "字符串为空"
fi
结果为:
字符串a不等于字符串b
字符串a不等于字符串b
字符串a的长度不为0
字符串a的长度不为0
字符串不为空
3.6.6 文件测试运算符
下表列出了常用的文件测试运算符
| 运算符 | 说明 |
|---|---|
| -b file | 检测文件是否是块设备文件,如果是,则返回true |
| -c file | 检测文件是否是字符设备文件,如果是,则返回true |
| -d file | |
| -f file | |
| -g file | |
| -k file | |
| -p file | |
| -u file | |
| -r file | |