Shell 简介
管理整个计算机硬件的其实就是操作系统的核心(kernel),用户一般是通过 Shell 来和 kernel 沟通,来达到我们想要的工作。
Shell 脚本就是包含一组可运行的特定 Shell 命令(在这里指 bash shell)的文本文件,命令的执行与其出现在脚本中的顺序一致。
Shell 提供了一种方式让人可以方便的调用操作系统命令库的接口,便于简化那些人们不愿意操作的重复而繁杂的工作,比如 web 应用程序的部署,特别是一套多服务系统,尤其是对于新手来说更是灾难。对于一些小公司来说,没能那么幸运拥有一位高级运维人员,这就对开发提出了更高的要求,需要熟练使用 shell 脚本实现自动化部署系统。拿我们公司举例来说,之前没有自动化部署,装完数据库后,部署应用得花半天时间,用上自动部署后可以在半小时内部署好,只需输入一些自定义信息就可以,比如端口。
除了这些还可以用来做比如:定时删除日志文件、Web 爬取、磁盘用量跟踪、天气数据下载、文件更名,等等。
常见的 shell 指令我们肯定经常会使用,比如:cd、ll、ls -la、vim。bash 的功能里面最棒的一个就是它能记住使用过的指令,你只要摁上下键,就可以找到前/后一个指令,非常方便,默认可以记住多达 1000 个的指令!
我们可以使用 alias 来帮助我们减少输入,比如下达命令设定别名:$ alias lm = ‘ls -la’,这样就可以使用 lm 代替 ls -la。
Shell 变量
自定义变量和环境变量
在 Shell 变量中包括自定义变量和环境变量:
自定义变量:脚本中自己命名定义的变量,通常为局部变量,其他 Shell 程序不能访问到;
环境变量,操作系统已定义的变量,如 PATH,所有 Shell 程序都能访问到,也可以通过 env 查看所有环境变量。 可以通过 echo $ORACLE_HOME 来查看所有环境变量。
常见的环境变量有:
变量名 | 作用 |
---|---|
PATH | 决定了 Shell 将从哪些目录下查找程序或命令 |
LANG | 操作系统字符集 |
HOME | 当前用户主目录,如 oracle 的主目录为:/home/oracle |
HISTSIZE | 历史记录数,可以记住的 shell 指令数 |
HOSTNAME | 指主机的名称 |
SHELL | 当前用户 Shell 类型,如:/bin/bash |
命名
变量名由字母、数字、下划线组成,如:url_1。
只能以字母或者下划线开头,如:route_path、_pig。
不能使用 Shell 的保留字。
不能使用空格、不能使用标点符号。
赋值、调用、删除
- 新建 hello.sh 脚本,扩展名不影响执行:
1 2 3 4 |
#!/bin/bash #自定义变量 hello hello="Hello world!"; //注意等号两边不能有空格 echo $hello; |
-
给 hello.sh 脚本赋予执行权限:
chmod +x hello.sh
。 -
调用脚本 ./hello.sh,hello.sh 的前面需要加上 ./,这是因为不加上的话会去 PATH 下查找是否有对应的命令可以执行,而 PATH 下只有 /bin、/sbin、/usr/bin、/usr/sbin,通常当前目录不在 PATH 下,就会提示 comman not found,所以要用 ./ 告诉系统在当前目录下查找。
. 使用 unset 命令可以删除变量:
unset variable_name
例如:
1 2 3 4 5 6 7 |
#!/bin/bash #自定义 hello hello="Hello world!" echo "hello="$hello unset hello echo "hello="$hello |
输出结果:
1 2 3 |
[oracle@195 ~]$ ./hello.sh hello=Hello world! hello= |
Shell 字符串
单引号、双引号
Shell 中最常用的数据类型就是字符串和数字,除此之外也没有数据类型了,在 Shell 中字符串可以用单引号或者双引号来包围,如:‘/opt/IBM’、"/opt/IBM"、/opt/IBM。也可以没有引号包围。
两者的区别在于:
-
单引号包围的字符串会以原样输出,如:echo ‘hello ${name}’,会输出:hello ${name},且单引号包围的字符串中不能出现单引号,进行转义也不行。
-
双引号包围的字符串会将变量进行替换,如:echo "hello ${name}",会输出:hello jack,且双引号包围的字符串中可以出现双引号,进行转义就行。
不被引号包围的字符串,会对变量进行解析,这点跟双引号一样,但是字符串中不能出现空格,否则空格后面的字符串会被当作命令或者其他变量解析。
综上,当有变量时最好是使用双引号包围字符串,且变量最好有 {} 包围,明确变量名,这也是最佳编程实践。
获取字符串长度
获取字符串长度的方式有两种:
1 2 |
${#string_name} expr length string_name |
举个例子:
1 2 3 4 5 |
#!/bin/bash #自定义 hello hello="Hello world!" echo "length of hello is:"${#hello} expr length "$hello" |
输出:
1 2 3 |
[oracle@195 ~]$ ./hello.sh length of hello is:12 12 |
截取字符串
使用 # 截取右边字符,方式为 ${hello#*chars},例如:
1 2 3 4 |
#!/bin/bash #自定义 hello hello="Hello world!" echo ${hello#*o} |
输出:
1 2 3 |
[oracle@195 ~]$ ./hello.sh world! |
表明截取了从左到右第一个 o 右边的字符串,*chars 代表忽略 chars 左边任意长度的字符串(包括 chars)。如果不写星号,那么就不会忽略 chars 左边的字符串。
如果像直到最后一个 chars 再截取右边的字符串,那么可以用 ## 来截取,例如:
1 2 3 4 |
#!/bin/bash #自定义 hello hello="Hello world!" echo ${hello##o} |
输出:
1 2 |
[oracle@195 ~]$ ./hello.sh rld! |
使用 % 截取左边字符串,方式为:${hello%chars}。与 # 不同的是,这里在右边,代表忽略 chars 右边的任意长度字符串,来截取 chars 左边的字符串,其用法跟 # 类似。
看个例子:
1 2 3 4 |
#!/bin/bash #自定义 hello hello="Hello world!" echo ${hello%o*} |
输出:
1 2 3 |
[oracle@195 ~]$ ./hello.sh Hello w |
可以看到“%”截取时是从右边开始的,从右往左,也可使用“%%”直到最后一个 chars 再截取左边的字符串。
Shell 数组
Shell 数组只能是一维的,不支持多维数组,并且 Shell 是弱类型的,数组中的类型不一定只有一种,且不限制数组的长度大小,理论上可以是无限大小。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#!/bin/bash #创建数组 array=(a b c d e) #获取数组长度 length1=${#array[@]} echo "length1=${length1}" #另一种获取数组长度方法 length2=${#array[*]} echo "length2=${length2}" #获取第三个元素 echo ${array[2]} #删除第二个元素 unset array[1] #输出整个数组 echo ${array[@]} #for 遍历整个数组 for i in ${array[@]};do echo $i;done; #删除整个数组 unset array #查看是否已删除 for i in ${array[@]};do echo $i;done; |
输出:
1 2 3 4 5 6 7 8 9 |
[oracle@195 ~]$ ./test_array.sh length1=5 length2=5 c a c d e a c d e |
Shell 基本运算符
算数运算符
包括:+、-、*、/、%、=、==、!=
这些跟我们其他编程语言遇到的是一样的,就不详细说明,举个简单例子。
1 2 3 4 5 |
#!/bin/bash a=1; b=2; c=`expr $a + $b`; echo "total is: {c}"; |
输出:
1 2 |
[oracle@195 shelldir]$ ./test_operation.sh total is: 3 |
关系运算符
包括:-eq、-nq、-gt、-lt、-ge、-le
运算符 | 说明 |
---|---|
-eq | 判断两个数据是否相等 |
-nq | 判断两个数据是否不相等 |
-gt | 判断左边的数据是否大于右边的数据 |
-lt | 判断左边的数据是否小于右边的数据 |
-ge | 判断左边的数据是否大于等于右边的数据 |
-le | 判断左边的数据是否小于等于右边的数据 |
接下来看下程序怎么写:
1 2 3 4 5 6 7 8 9 |
#!/bin/bash a=100; b=99; if [ $a -eq $b ] then echo "A" else echo "B" fi |
输出:
1 2 |
[oracle@195 shelldir]$ ./test_operation.sh B |
其他关系运算符相信大家都应该知道怎么使用了。
逻辑运算符
包括:&&、||
运算符 说明
&& 逻辑与,左右两边的表达式都为 true 才 true,有一个为 false 则为 false
\ \
接下来看下程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#!/bin/bash a=100; b=99; c=90; if [[ $a -gt $b && $a -gt $c ]] then echo "A" elif [[ $b -gt $a && $b -gt $c ]] then echo "B" else echo "C" fi |
输出:
1 2 |
[oracle@195 shelldir]$ ./test_operation.sh A |
布尔运算符
包括:!、-o、-a
运算符 说明
! 非运算,对表达式取反,如:[ !false ] 返回 false
-o 或运算,左右两边的表达式存在 true 则为 true,都为 false 则为 false
-a 与运算,左右两边的表达式存在 false 则为 false,都为 true 则为 true
接下来看下程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#!/bin/bash a=100; b=99; c=90; if !((a == b)) then echo "a 与 b 不相等" else echo "a 与 b 相等" fi if [ $a -gt $b -a $a -gt $c ] then echo "A" elif [ $b -gt $a -a $b -gt $c ] then echo "B" else echo "C" fi |
输出:
a 与 b 不相等
A
-a 与 -o 用法相同。
字符串运算符
包括:=、!=、-z、-n、str
运算符 说明
= 检测两个字符串是否相等,相等返回 true
!= 检测两个字符串是否不相等,不相等则返回 true
-z 检测字符串长度是否为 0,为 0 则返回 true
-n 检测字符串长度是否为 0,不为 0 则返回 true
str 检测字符串是否为空,不为空则返回 true
接下来一起看下程序:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
#!/bin/bash a="zoom" b="" if [ -n $a ] then echo "a 长度不为 0" else echo "a 长度为 0" fi if [ -z $b ] then echo "b 长度为 0" else echo "b 长度不为 0" fi if [ $b ] then echo "b 不为空字符串" else echo "b 是空字符串" fi |
输出:
1 2 3 4 |
a 长度不为 0 b 长度为 0 b 是空字符串 |
Shell 流程控制
if 条件语句
前面我们也看到了 if 条件语句的使用,基本语法就是:
1 2 3 4 5 6 7 8 9 10 |
#!/bin/bash if conditionA then exprA elif conditionB then exprB else exprC fi |
这就是 if-elif-else,最后别忘了 fi 作为结尾。跟 Java 不同的是,Shell 的 if 不能有空语句,即什么都不做的条件表达式。还有一点是 condition 一般用中括号[],Java 都是用小括号 ()。
for 循环语句
for 循环语句的基本语法是:
1 2 3 4 5 6 7 8 |
#!/bin/bash for loop in item1 item2 ... itemN do condition1 condition2 ... conditionP done |
其中,for loop in 1 2 3 4 5 可被替换成 for loop in {1..5}。以及 类似于 java 中的 for 条件语句写法:for((i=0;i<=5;i++))。
举个例子:
1 2 3 4 5 |
#!/bin/bash for((i=0;i<=5;i++)) do echo "num is ${i}" done |
输出:
1 2 3 4 5 6 |
num is 0 num is 1 num is 2 num is 3 num is 4 num is 5 |
while 循环语句
有了上面的 if 和 for 之后,相信可以实现很多的逻辑控制了,接下来再看看 while 循环语句怎么写。
基本语法是:
1 2 3 4 5 |
#!/bin/bash while conditionA do commandA done |
看个例子:
1 2 3 4 5 6 7 |
#!/bin/bash i=0 while(( i<=5 )) do echo "num is ${i}" let i++ done |
输出:
1 2 3 4 5 6 |
num is 0 num is 1 num is 2 num is 3 num is 4 num is 5 |
while 语句还可以用来接收用户输入,用法是:
while read choose
无限循环是经常会用到的一个语法,下面列举了无限循环的三种实现方式:
1 2 3 |
1. while : 2. while true 3. for(( ; ; )) |
Shell 函数
了解完了上面的流程控制语句,下面我们来看看 Shell 是怎么定义函数,以及调用函数的,我们先来看个简单的例子。
1 2 3 4 5 6 7 8 9 10 11 12 |
#!/bin/bash printnum(){ echo "my first function" } echo "call funtion start" printnum echo "call funtion end" 输出: call funtion start my first function call funtion end |
这个是不带参数没有返回值的例子,接下来我们来看看不带参数没有返回值的例子:
1 2 3 4 5 6 7 8 |
#!/bin/bash plusnum(){ a=1 b=3 return $((a+b)) } plusnum echo "a+b="$? |
输出:
a+b=4
从上面我们可以看出,在调用函数 plusnum 后,通过$?可以拿到返回结果。而调用方式是直接用函数名即可,不需要加上括号,这点跟 Java 和其他面向对象的编程语言都有所不同。
下面再看一个有参数的函数:
1 2 3 4 5 6 7 8 |
#!/bin/bash plusnum(){ a=$1 b=$2 return $((a+b)) } plusnum 99 100 echo "a+b="$? |
从上面我们可以看到函数 plusnum 中通过 $1 和 $2 获取传入的参数,在相加后返回结果,调用时直接跟在函数后面,并不像 Java 的函数传参。
理解一个 Shell 脚本
其实前面举得例子都比较简单,可以认为前面的语法是 Shell 程序的一个骨架,而众多的 Shell 命令以及业务内容将是填充进去的血肉,下面我们利用前面学过的知识来理解下下面的这个 Shell 程序。
首先他先定义了一个 DIR_HOME 数组
然后定义变量 FLAG、OFFICE_HOME,值为空
$(cd "$(dirname "$0")";pwd) 意为取当前目录
输出环境变量 KKFILEVIEW_BIN_FOLDER
进入目录
输出目录
查询 application.properties 中不以 # 开头的 office.home 的数量
如果上面的值为 0,那么就认为用了自定义的 office.home
否则循环最开始定义的 DIR_HOME 数组,看看其中是否有 soffice.bin 文件,若有则修改 FLAG 的值为 true,OFFICE_HOME 赋值为当前 DIR_HOME 数组的值,并且跳出循环
接下来判断 FLAG 是否为空,如果为空说明系统中从未安装过 OpenOffice,则调用 install.sh 进行安装,否则将认为已安装 OpenOffice,并输出安装路径
下面开始正式启动应用程序
总结
通过 Shell 脚本可以大大简化我们的工作,使我们从日复一日的重复劳动中解放出来,有时间去尝试更有趣的事物。学了技术之后就赶紧动手尝试起来吧,有些东西看看都会,一写起来就不会了,所以玩过之后印象才会更加深刻。
Views: 22