天下事有难易乎?为之,则难者亦易矣;不为,则易者亦难矣。

写好Shell脚本,自动化部署就不愁了!

itzoo 463次浏览 0个评论

点击“终码一生”,关注,置顶公众号

每日技术干货,第一时间送达!



做 Java 的肯定都接触过 Linux 系统,那么很多时候我们在开发的过程中都是把我们项目打成一个jar包,或者是war包的形式,然后通过 XFTP 上传到我们服务器的指定目录,然后运行一端启动脚本,让我们的项目变得可以访问 就像 ./sh service.sh start 然后启动我们写好的 sh 的shell脚本。接下来我们就来学习一下关于 Shell 脚本是如何写出来的。



1

Shell 脚本


Shell 脚本是什么?


Shell是一个命令解释器,它的作用是解释执行用户输入的命令及程序等,也就是说,我们用户每输入一条命令,Shell 就会相对应的执行一条命令。当命令或程序语句不在命令行下执行,而是通过一个程序文件来执行时,该程序文件就被称为Shell脚本。


在我们的 Shell 脚本中,会有各种各样的内容,赋值,计算,循环等一系列的操作,接下来我们就来看看这个 Shell 脚本怎么写吧!


1.查看自己当前系统默认的 Shell


echo $SHELL


输出:/bin/bash


2.查看系统支持的Shell


cat /etc/shells


输出:/bin/sh /bin/bash /usr/bin/sh /usr/bin/bash

也就是说,我们的云服务器是支持我们在这里给他安排 Shell 脚本的


 

2

Shell 脚本怎么写出来的


我们这时候先来安排一下 sh 的文件,创建一个文件夹,然后在其中创建一个 sh 的文件。


mkdir /usr/local/shelltest
touch test.sh


创建完成我们编辑一下内容


vim test.sh


#!/bin/bash
echo "Hello World Shell"


然后我们出来运行一下我们的 Shell 的第一个脚本


bash test.sh


出来的结果是 Hello World Shell


一个及其简单的脚本出现了,接下我们就分析一波我们写了点啥?


#!/bin/bash
#! 是一个约定的标记,它告诉系统这个脚本需要什么解释器来执行,即使用哪一种 Shell


我们在之前也使用了 echo $SHELL 来查看了自己系统默认的是哪一种 sh 解析器,之前看到的是/bin/bash,所以我们在写 Shell 脚本的时候,我们在开头默认的约定中,我们写了这个是用 /bin/bash 来进行解释的,


那么我们如何像之前调用我们的当前目录中的 Shell 脚本一样去调用他呢?就像这个样子的 ./sh service.sh start


1.授权


我们先不授权试一下看看能通过 ./test.sh 进行调用么


bash: ./test.sh: Permission denied 会提示这个,也就是没有授权定义,


授权命令:chmod +x test.sh


2.执行 ./test.sh


然后调用就能正常输出了,就是说,在当前的目录下执行这个脚本命令。


 

3

Shell 脚本的变量

 

定义变量和使用


变量命名实际上很简单,我们先来试一下


name=zhiyikeji


这时候我们怎么使用变量呢?实际上只要在前面加上一个符号就可以 $


echo $name


[root@iZbp10j01t7sgfqekyefpoZ ~]# echo $name
zhiyikeji

[root@iZbp10j01t7sgfqekyefpoZ ~]# echo ${name}
zhiyikeji


上面的两种写法都是可以的,外面的大括号加和不加区别不大,可以省略,直接就$name 就可以使用你定义的变量


使用括号的意义一般在于区别某些变量,比如你写了一串的内容,可能写的是 echo $nameismyfriend,如果连在一起,是不是有点尴尬,这时候就可以使用括号区别一下,echo ${name}ismyfriend 不使用括号的时候,他就去找nameismyfriend这个变量了,就无法出来我们要的效果。


删除自己定义的变量


unset name


这时候我们就把我们刚才定义的 name=zhiyikeji 这个变量给去掉了,我们可以调用一下我们的变量看是什么?


echo $name


[root@iZbp10j01t7sgfqekyefpoZ ~]# unset name
[root@iZbp10j01t7sgfqekyefpoZ ~]
# echo $name


这是不是就证明我们自己定义的变量已经删除了


只读变量


那么我们需要一个关键字,大家肯定能想到是什么关键字 readonly


我们先给name赋值,然后使用 readonly 设置只读,然后再改变一下试试,


[root@iZbp10j01t7sgfqekyefpoZ ~]# name=zhiyikeji
[root@iZbp10j01t7sgfqekyefpoZ ~]# echo $name
zhiyikeji
[root@iZbp10j01t7sgfqekyefpoZ ~]# readonly name
[root@iZbp10j01t7sgfqekyefpoZ ~]# echo $name
zhiyikeji
[root@iZbp10j01t7sgfqekyefpoZ ~]# name=ceshi
-bash: name: readonly variable
[root@iZbp10j01t7sgfqekyefpoZ ~]#


竟然是真的,如果不设置只读,是不是会重新可以进行赋值,我们测试个年龄,


[root@iZbp10j01t7sgfqekyefpoZ ~]# age=10
[root@iZbp10j01t7sgfqekyefpoZ ~]# echo $age
10
[root@iZbp10j01t7sgfqekyefpoZ ~]# age=20
[root@iZbp10j01t7sgfqekyefpoZ ~]# echo $age
20


所以我们就可以肯定,readonly就是设置只读的关键词,记住了么?


那么设置只读的变量可以删除么?毕竟总有杠精的面试官会提问这个棘手的问题,但是,阿粉试过的所有方式好像都是不行的,阿粉就直接重启了自己的服务器,这样临时的变量就不存在了!



4

Shell 脚本的流程控制


说真的,Shell脚本的流程控制数一般才是yyds,为什么这么说,因为你在写大部分的脚本的时候,流程控制的地方永远是最多的,判断,选择,等等一系列的函数,当时熟练使用的时候,就发现这东西确实很有意思。


IF


我们先说最简单的 if else 这也是我们最经常使用的判断,在写 Shell 脚本的时候,就不像我们的 Java 中直接写


if(...){
    ....
}else{
    ....
}


Xshell 中的语法就不是这个样子的,Xshell语法:


if ...
then
    ...
else
    ...
fi


末尾的 fi 就是 if 倒过来拼写,我们可以写一个 if 的脚本试一下这个流程能否理解。


#! /bin/bash
  

if [ $1 -gt 2 ];

then
        echo "值大于2"
else
        echo "值小于2"
        exit
fi


这里申明一下,


  • -ge标识的是大于等于符号;

  • -le表示的是小于等于符号;

  • -gt 表示大于符号;

  • -lt 表示小于符号;

  • -eq 表示等于符号;

  • -ne 表示不等于符号;


我们在上面这段脚本中写就是内容就是,我们给脚本传入一个值,然后比对这个值和2的大小关系,然后输出我们指定的内容。


运行后就能看到


[root@iZbp10j01t7sgfqekyefpoZ shelltest]# sh test2.sh 1
值小于2
[root@iZbp10j01t7sgfqekyefpoZ shelltest]# sh test2.sh 3
值大于2


$1 表示我们给 Shell 脚本输入的第一个参数, $0就是你写的shell脚本本身的名字,$2 是我们给 Shell 脚本传的第二个参数


大家在部署某些项目的时候,是不是启动命令就很简洁,就是 sh service.sh start 类似这种的,那我们来看看一般这种是怎么写的,这就用到了另外一块的内容,和 if 类似,在 Java 中也有,那就是 Case.


Case


我们先来看看 Case 的语法,


case … esac 实际上就和 Java 中的 Case 是非常相似的,case 语句匹配一个值与一个模式,如果匹配成功,执行相匹配的命令.esac是一个结束的标志。


case 值 in
匹配值1)
    command1
    command2
    
    ;;
匹配值2)
    command1
    command2
    ;;
esac


光说不练,假把式,我们来搞一下试试写一个脚本来搞一下。就用我们刚才说的 sh servic.sh start 来进行测试。


case $1 in
  
start)
        #输出启动命令
        echo "start已经开始"
        ;;
stop)
        #输出停止命令
        echo "stop命令执行"
        ;;
esac
exit


我们来看看运行结果


[root@iZbp10j01t7sgfqekyefpoZ shelltest]# sh service.sh start
start已经开始
[root@iZbp10j01t7sgfqekyefpoZ shelltest]# sh service.sh stop
stop命令执行


那么这段 Shell 脚本是什么意思呢?其实很简单,匹配我们传入的第一个字符,和 start 还有 stop 进行比较,如果匹配上之后,输出命令,最后退出即可。


是不是感觉没有那么复杂了呢?













1、SSM框架简介

SSM框架是Spring MVC ,Spring和Mybatis框架的整合,是标准的MVC模式,将整个系统划分为View层,Controller层,Service层,DAO层四层,使用Spring MVC负责请求的转发和视图管理,Spring实现业务对象管理,Mybatis作为数据对象的持久化引擎。


2、SSM框架各层介绍

2.1、持久层(Mybatis):Dao层(mapper)

DAO层:DAO层主要是做数据持久层的工作,负责与数据库进行联络的一些任务都封装在此。


DAO层的设计首先是设计DAO的接口。

然后在Spring的配置文件中定义此接口的实现类。

然后就可在模块中调用此接口来进行数据业务的处理,而不用关心此接口的具体实现类是哪个类,显得结构非常清晰。

DAO层的数据源配置,以及有关数据库连接的参数都在Spring的配置文件中进行配置。

2.2、业务层(Spring):Service层

Service层:Service层主要负责业务模块的逻辑应用设计。


首先设计接口,再设计其实现的类。

接着再在Spring的配置文件中配置其实现的关联。这样我们就可以在应用中调用Service接口来进行业务处理。

Service层的业务实现,具体要调用到已定义的DAO层的接口。

封装Service层的业务逻辑有利于通用的业务逻辑的独立性和重复利用性,程序显得非常简洁。

2.3、表现层(springMVC):Controller层(Handler层)

Controller层:Controller层负责具体的业务模块流程的控制。


在此层里面要调用Service层的接口来控制业务流程。

控制的配置也同样是在Spring的配置文件里面进行,针对具体的业务流程,会有不同的控制器,我们具体的设计过程中可以将流程进行抽象归纳,设计出可以重复利用的子单元流程模块,这样不仅使程序结构变得清晰,也大大减少了代码量。

2.4、视图层:View层

View层:View层与控制层结合比较紧密,需要二者结合起来协同工发。View层主要负责前台jsp页面的表示。


3、SSM框架各层关系

DAO层、Service层这两个层次都可以单独开发,互相的耦合度很低,完全可以独立进行,这样的一种模式在开发大项目的过程中尤其有优势。

Controller,View层因为耦合度比较高,因而要结合在一起开发,但是也可以看作一个整体独立于前两个层进行开发。这样,在层与层之前我们只需要知道接口的定义,调用接口即可完成所需要的逻辑单元应用,一切显得非常清晰简单。

Service层是建立在DAO层之上的,建立了DAO层后才可以建立Service层,而Service层又是在Controller层之下的,因而Service层应该既调用DAO层的接口,又要提供接口给Controller层的类来进行调用,它刚好处于一个中间层的位置。每个模型都有一个Service接口,每个接口分别封装各自的业务处理方法。

4、SSM原理及流程

客户端发送请求到DispacherServlet(分发器)

由DispacherServlet控制器查询HanderMapping,找到处理请求的Controller

Controller调用Service业务逻辑层处理后返回结果


一、什么是哈希表


在讨论哈希表之前,我们先大概了解下其他数据结构在新增,查找等基础操作执行性能


数组:采用一段连续的存储单元来存储数据。对于指定下标的查找,时间复杂度为O(1);通过给定值进行查找,需要遍历数组,逐一比对给定关键字和数组元素,时间复杂度为O(n),当然,对于有序数组,则可采用二分查找,插值查找,斐波那契查找等方式,可将查找复杂度提高为O(logn);对于一般的插入删除操作,涉及到数组元素的移动,其平均复杂度也为O(n)


线性链表:对于链表的新增,删除等操作(在找到指定操作位置后),仅需处理结点间的引用即可,时间复杂度为O(1),而查找操作需要遍历链表逐一进行比对,复杂度为O(n)


二叉树:对一棵相对平衡的有序二叉树,对其进行插入,查找,删除等操作,平均复杂度均为O(logn)。


哈希表:相比上述几种数据结构,在哈希表中进行添加,删除,查找等操作,性能十分之高,不考虑哈希冲突的情况下(后面会探讨下哈希冲突的情况),仅需一次定位即可完成,时间复杂度为O(1),接下来我们就来看看哈希表是如何实现达到惊艳的常数阶O(1)的。


我们知道,数据结构的物理存储结构只有两种:顺序存储结构和链式存储结构(像栈,队列,树,图等是从逻辑结构去抽象的,映射到内存中,也这两种物理组织形式),而在上面我们提到过,在数组中根据下标查找某个元素,一次定位就可以达到,哈希表利用了这种特性,哈希表的主干就是数组。


比如我们要新增或查找某个元素,我们通过把当前元素的关键字 通过某个函数映射到数组中的某个位置,通过数组下标一次定位就可完成操作。


查找操作同理,先通过哈希函数计算出实际存储地址,然后从数组中对应地址取出即可。


哈希冲突


然而万事无完美,如果两个不同的元素,通过哈希函数得出的实际存储地址相同怎么办?也就是说,当我们对某个元素进行哈希运算,得到一个存储地址,然后要进行插入的时候,发现已经被其他元素占用了,其实这就是所谓的哈希冲突,也叫哈希碰撞。前面我们提到过,哈希函数的设计至关重要,好的哈希函数会尽可能地保证 计算简单和散列地址分布均匀,但是,我们需要清楚的是,数组是一块连续的固定长度的内存空间,再好的哈希函数也不能保证得到的存储地址绝对不发生冲突。那么哈希冲突如何解决呢?哈希冲突的解决方案有多种:开放定址法(发生冲突,继续寻找下一块未被占用的存储地址),再散列函数法,链地址法,而HashMap即是采用了链地址法,也就是数组+链表的方式。


二、HashMap的实现原理


HashMap的主干是一个Entry数组。Entry是HashMap的基本组成单元,每一个Entry包含一个key-value键值对。(其实所谓Map其实就是保存了两个对象之间的映射关系的一种集合)


简单来说,HashMap由数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的,如果定位到的数组位置不含链表(当前entry的next指向null),那么查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度为O(n),首先遍历链表,存在即覆盖,否则新增;对于查找操作来讲,仍需遍历链表,然后通过key对象的equals方法逐一比对查找。所以,性能考虑,HashMap中的链表出现越少,性能才会越好。

Shell是系统的用户界面,提供了用户与内核进行交互操作的一种接口。它接收用户输入的命令并把它送入内核去执行。

实际上Shell是一个命令解释器,它解释由用户输入的命令并且把它们送到内核。不仅如此,Shell有自己的编程语言用于对命令的编辑,它允许用户编写由shell命令组成的程序。Shell编程语言具有普通编程语言的很多特点,比如它也有循环结构和分支控制结构等,用这种编程语言编写的Shell程序与其他应用程序具有同样的效果。

Linux提供了像MicrosoftWindows那样的可视的命令输入界面–X Window的图形用户界面(GUI)。它提供了很多桌面环境系统,其操作就像Windows一样,有窗口、图标和菜单,所有的管理都是通过鼠标控制。GNOME。

每个Linux系统的用户可以拥有他自己的用户界面或Shell,用以满足他们自己专门的Shell需要。


同Linux本身一样,Shell也有多种不同的版本。主要有下列版本的Shell:


Bourne Shell:是贝尔实验室开发的。

BASH:是GNU的Bourne Again Shell,是GNU操作系统上默认的shell。

Korn Shell:是对Bourne SHell的发展,在大部分内容上与Bourne Shell兼容。

C Shell:是SUN公司Shell的BSD版本。

Z Shell:The last shell you’ll ever need! Z是最后一个字母,也就是终极Shell。它集成了bash、ksh的重要特性,同时又增加了自己独有的特性。

传统意义上的shell指的是命令行式的shell,以后如果不特别注明,shell是指命令行式的shell。

文字操作系统与外部最主要的接口就叫做shell。shell是操作系统最外面的一层。shell管理你与操作系统之间的交互:等待你输入,向操作系统解释你的输入,并且处理各种各样的操作系统的输出结果。

shell提供了你与操作系统之间通讯的方式。这种通讯可以以交互方式(从键盘输入,并且可以立即得到响应),或者以shell script(非交互)方式执行。shell script是放在文件中的一串shell和操作系统命令,它们可以被重复使用。本质上,shell script是命令行命令简单的组合到一个文件里面。

Shell基本上是一个命令解释器,类似于DOS下的COMMAND.COM。它接收用户输入的命令(如ls等),然后调用相应的应用程序。较为通用的shell有标准的Bourne shell和C Shell。

交互式shell和非交互式shell

交互式模式就是shell等待用户的输入,并且执行用户提交的命令。这种模式被称作交互式是因为shell与用户进行交互。这种模式也是大多数用户非常熟悉的:登录、执行一些命令、签退。当用户签退后,shell也终止了。

shell也可以运行在另外一种模式:非交互式模式。在这种模式下,shell不与用户进行交互,而是读取存放在文件中的命令,并且执行它们。当它读到文件的结尾,shell也就终止了。

Unix shell的类型

在UNIX中主要有:

Bourne shell(sh)

Korn shell(ksh)

Bourne Again shell(bash)

POSIX shell(sh)

C shell(csh)

TENEX/TOPS C shell(tcsh)

Bourne Shell(sh)

首个重要的标准Unix Shell是1979年底在V7 Unix(AT&T第7版)中引入的,并且以它的创始科技部基础条件平台“国家气象网络计算应用节点建设”(2004DKA50730)资助者Stephen Bourne的名字命名。Bourne shell 是一个交换式的命令解释器和命令编程语言。Bourne shell可以运行为login shell或者login shell的子shell(subshell)。只有login命令可以调用Bourne shell作为一个login shell。此时,shell先读取/etc/profile文件和$HOME/.profile文件。/etc/profile文件为所有的用户定制环境,$HOME/.profile文件为本用户定制环境。最后,shell会等待读取你的输入。

C shell(csh)

Bill Joy于20世纪80年代早期,在加利福尼亚大学伯克利分校开发了csh。它主要是为了让用户更容易地使用交互式功能,并把ALGOL风格的语法结构变成了C语言风格。它新增了命令历史、别名、文件名替换、作业控制等功能。

Korn Shell(ksh)

有很长一段时间,只有两类shell供人们选择——Bourne shell用来编程,csh用来交互。为了改变这种状况,AT&T贝尔实验室的David Korn开发了ksh。ksh结合了所有的C shell的交互式特性,并融入了Bourne shell的语法。因此,ksh广受用户的欢迎。它还新增了数学计算,进程协作(coprocess)、行内编辑(inline editing)等功能。ksh是一个交互式的命令解释器和命令编程语言。它符合POSIX——一个操作系统的国际标准。POSIX不是一个操作系统,而是一个目标在于应用程序的移植性的标准——在源程序一级跨越多种平台。

Bourne Again Shell(bash)

bash是GNU计划的一部分,用来替代Bourne shell。它用于基于GNU的系统如Linux。大多数的Linux(Red Hat、Slackware、Caldera)都以bash作为缺省的shell,并且运行sh时,其实调用的是bash。

POSIX Shell(sh)

POSIX shell是Korn shell的一个变种. 当前提供POSIX shell的最大卖主是Hewlett-Packard。在HP-UX 11.0,POSIX shell 就是/bin/sh,而bsh是/usr/old/bin/sh。

各主要操作系统下缺省的shell:

AIX下是Korn Shell。

Solaris缺省的是Bourne shell。

FreeBSD缺省的是C shell。

HP-UX缺省的是POSIX shell。

Linux是Bourne Again shell。

Windows Powershell

Windows PowerShell是一种新的交互式的命令行和基于任务脚本编写技术,它使信息技术(IT)管理员能够全面地自动操作和控制系统管理任务,从而提高了管理员的生产力。Windows PowerShell包括多个系统管理实用工具、一致的语法和命名惯例、及对普通管理数据更好地导航,如登记、证书存储或Windows Management Instrumentation(WMI)。Windows PowerShell还专门针对IT管理,提供直观的脚本编写语言。

Shell也是一个VB函数,它的作用是运行程序,语法是Shell(命令字符串[,窗口类型])



For


说到流程控制,那么肯定不能不说 for , 毕竟 for 循环在 Java 中那可是重头戏。


我们先看他的格式


for i in item1 item2 ... itemN
do
    command1
    command2
    ...
    commandN
done


那么我们有没有说像是 Java 中那种 for 循环一样的方式呢?比如说这个for ((i=1; i<=j; i++))


实际上也是支持这种的,我们来写一个试试。


j=$1
for ((i=1;i<=j;i++))
do
        echo $i
done


执行一下看看


[root@iZbp10j01t7sgfqekyefpoZ shelltest]# sh fortest.sh 6
1
2
3
4
5
6


既然有 for 那是不是就有 while 呢?是的,没错,确实是有 while ,也是循环的意思,但是写法有略微不一样的地方


While
while condition
do
    command
done


我们来举个尝试打印九九乘法表来看一下


a=1
b=1
while ((a <=9))
do
        while ((b<=a))
                do
                        let "c=a*b"   #声明变量c
                        echo -n "$a*$b=$c "
                        let b++
                done
                        let a++

     let b=1 #因为每个乘法表都是1开始乘,所以b要重置

             echo "" #显示到屏幕换行

     done


[root@iZbp10j01t7sgfqekyefpoZ shelltest]# sh whileTest.sh 
1*1=1
2*1=2 2*2=4
3*1=3 3*2=6 3*3=9
4*1=4 4*2=8 4*3=12 4*4=16
5*1=5 5*2=10 5*3=15 5*4=20 5*5=25
6*1=6 6*2=12 6*3=18 6*4=24 6*5=30 6*6=36
7*1=7 7*2=14 7*3=21 7*4=28 7*5=35 7*6=42 7*7=49
8*1=8 8*2=16 8*3=24 8*4=32 8*5=40 8*6=48 8*7=56 8*8=64
9*1=9 9*2=18 9*3=27 9*4=36 9*5=45 9*6=54 9*7=63 9*8=72 9*9=81


是不是也挺简单的?


其实 Shell 脚本的编写一般都是在实际应用中提升,单纯的写测试脚本,也是可以让自己对知识的掌握比较充分,而我们一般都是写一些比较简单的脚本,复杂的不是还有运维么?


PS:防止找不到本篇文章,可以收藏点赞,方便翻阅查找哦。

 


终码一生
长按扫描二维码
关注我们 获取更多技术资讯
微信号:zhongmayisheng


往期推荐



Notion,程序员最后一款笔记软件!

告别 Maven,赶快使用他!

精选30个炫酷的数据可视化大屏(内含资源下载),拿走不谢!

腾讯三面:40亿个QQ号码如何去重?

SpringBoot + Redis:模拟 10w 人的秒杀抢单!

Java 8 的Stream流那么强大,你知道它的原理吗?



ITZOO版权所有丨如未注明 , 均为原创丨转载请注明来自IT乐园 ->写好Shell脚本,自动化部署就不愁了!
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址