Maven 插件学习之: shade 插件

maven shade plugin 插件允许把工程使用到的依赖打包到一个 uber-jar(单一 jar 包) 中并隐藏(重命名)起来。

Shade Plugin 绑定到 package 生命周期。

使用

<project>
  ...
  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.1</version>
        <configuration>
          <!-- put your configurations here -->
        </configuration>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>
  ...
</project>

实例

该插件允许我们选择最终打的包中包含或者去除那些包,具体可以参考官网

该插件也允许我们将一些类重定位到其他地方(Relocating Classes),如果 uber JAR 被其他项目所以来,直接使用 uber JAR artifact 依赖中的类可能导致和其他相同类的冲突。解决这种问题的方法之一,就是将类重新移动到新位置。官网

默认情况下,到执行 installed/deployed 时默认会生成两个 jar 包,一个以 -shaded 结尾,这个名字是可以配置的。

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-shade-plugin</artifactId>
        <version>3.2.1</version>
        <executions>
          <execution>
            <phase>package</phase>
            <goals>
              <goal>shade</goal>
            </goals>
            <configuration>
              <shadedArtifactAttached>true</shadedArtifactAttached>
              <shadedClassifierName>customName</shadedClassifierName> <!-- Any name that makes sense -->
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

创建可执行 jar 包,可以将入口添加进来。 官网

reference


2019-02-27 maven , maven-plugin , build , java

Cinnamon 桌面下 Applets 推荐

使用 Linux Mint 一些时候,真的发现有些功能和配置真的非常舒服,以前也写过一篇文章说的是 Cinnamon 桌面自带的 nemo 文件管理器,这可能是我用过的所有系统中自带文件管理器让我用的最舒服的了。所以这里再总结一篇 Cinnamon 下好用的 applets 。

Cinnamon 下所有的 applets 都存放在 ~/.local/share/cinnamon/applets/ 目录下,如果有安装包直接移动到该目录下即可。

Desktop Capture

一句话简单的说就是支持 Screenshot 和 Recorder,录屏和录制视频。

GPaste Reloaded

改写自 Gnome GPaste,提供粘贴板历史记录追踪的功能,可以快速找打粘贴板历史。

Download and Upload Speed

看名字就知道了,看上传下载网速的

Cheaty - CheatSheet keeper

针对主题提供便捷提示的 Cheatsheet。

总结

更多的 Applets 可以到 Cinnamon 的官网自行发掘。

reference


2019-02-22 cinnamon , linux-mint , applets , desktop , gui

maven 相关的错误

deploy 遇到 400 错误

错误日志

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-deploy-plugin:2.7:deploy (default-deploy) on project xxxx: Failed to deploy artifacts: Could not transfer artifact xxx:pom:1.0.1 from/to archiva.internal (http://nexus.xxx/nexus/content/repositories/releases/): Failed to transfer file: http://nexus.xxx/nexus/content/repositories/releases/xxx.pom. Return code is: 400, ReasonPhrase: Bad Request. -> [Help 1]

在 deploy 正式版本时,一般遇到 400 错误,去 Nexus 上看下是否版本已经存在,因为正式的版本不允许覆盖。


2019-02-20 maven , error

设计模式之行为模式

设计模式的行为模式关注类和对象如何交互,以及如何负责对应的事务,也就是怎么定义类的行为和职责。

模板方法模式

模板方法 (Template Method) 模式是类的行为模式。准备一个抽象类,将部分逻辑以具体方法以及具体构造函数的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。这就是模板方法模式的用意。

Java 的集合就是一个典型的,利用了模板方法模式的例子。Java 集合中的 Collection 集合包括 List 和 Set 两大组成部分。List 是队列,而 Set 是没有重复元素的集合。它们共同的接口都在 Collection 接口声明。

模板方法涉及到两个角色,抽象模板和具体模板。抽象模板定义:

  • 一个或者多个抽象操作
  • 定义并实现模板方法,给出顶级逻辑骨架,而逻辑组成的步骤在对应的抽象操作中,推迟到子类实现

具体模板:

  • 实现父类所有抽象方法
  • 每一个抽象模板角色都可以定义多个具体模板角色实现,从而使得顶级逻辑实现各不相同

观察者模式

观察者模式是对象的行为模式,又叫发布 - 订阅 (Publish/Subscribe) 模式、模型 - 视图 (Model/View) 模式、源 - 监听器 (Source/Listener) 模式或从属者 (Dependents) 模式。

观察者模式很好理解,举一个具体的例子,比如订阅报纸杂志,这里涉及到两个对象,一个是报社,一个是订阅者,订阅者订阅报纸,那么只要报纸有更新就送到订阅者,而如果订阅者取消订阅,那么就收不到更新了。理解这个模式就很好理解观察者模式了。

观察者模式定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。

java.util.Observable 的坑

很容易看到 JDK 中的 Observable 是一个类,不是一个接口,甚至没有实现任何一个接口。正因为这样,Observable 子类化的过程,不允许添加 Observable 的行为到 superclass,这就限制了重用这些类的可能。

另外应为没有 Observable 接口,那么就不能有自己的实现来和 Java 内置的 Observer 接口交互。

再次,看 Observable 类中的 setChanged() 方法,是 protected,这意味着你只能子类化 Observable,而不能创建一个 Observable 的实例然后将其放到新的对象中。

所以总结来说就是,如果能够继承 Observable 那就用 JDK 提供的额方式,如果想要更多的扩展性那就用自己的方式实现观察者模式,而不要使用 JDK 的方法。

迭代器模式

迭代器模式又叫游标 (Cursor) 模式,是对象的行为模式。迭代器模式可以顺序地访问一个聚集中的元素而不必暴露聚集的内部表象(internal representation)。


2019-02-19 design-pattern , java

启动挂载配置 fstab 文件

因为之前克隆系统 获知了 fstab 文件,用来在启动系统时挂载对应硬盘分区中的系统。打开我自己系统的文件之后也发现可以配置挂载其他 FAT 或者 NTFS 格式的 Windows 下的分区。而最近可能因为 SSD 挂掉的原因,系统无法启动,再次把 fstab 放到了重要的位置,所以才有了这样一篇文章,主要用来总结一下 /etc/fstab 文件的作用及配置。

fstab 的完整路径是 /etc/fstab,纯文本文件,root 用户用任意的文本编辑器打开即可。

fstab 是启动时配置文件,实际文件挂载是记录到 /etc/mtab/proc/mounts 两个文件。

文件配置格式

每一个硬盘分区都有一个 UUID,可以使用 blkid 命令来查看(需 root 权限),在确定每一个硬盘分区的 ID 之后再来查看 fstab 文件就能非常轻松的看到 fstab 中挂载的内容具体含义。

# <file system> <mount point>   <type>  <options>       <dump>  <pass>
UUID=b9999999-a25b-4ca0-b597-fc62e121aae1 none            swap    sw              0       0
UUID=89999999-e8f2-4e68-84a1-b82e79a041c7 / ext4 errors=remount-ro 0 1

上述两条配置可以看到一个挂载了 swap 分区,一个挂载了根分区,分别在两个分区中。其实 fstab 文件就是将 mount 命令挂载的参数写到了文件中。

各字段具体解释:

  • file system 挂载设备,指的是硬盘分区,光驱等设备,指定分区的名字
  • mount point 挂载点,挂载到系统的位置
  • type 文件系统类型,包括 ext3,ext4,swap,ntfs, auto 等等,auto 表示让 mount 命令自动判断文件类型
  • options 参数,挂载的设备开机自动加载,显示,读写权限的配置等等,mount 命令用法相关,具体参数配置见 man mount
  • dump 备份命令,dump utility 用来决定是否做备份,dump 检查 entry 并用数字来决定是否对文件系统进行备份。为 0,则忽略,为 1,则备份
  • pass 是否 fsck 检查扇区,启动过程中,系统默认会用 fsck 检查文件系统是否完整。但是有些文件系统不需要检查,比如 swap 或者特殊文件系统 /proc 或者 /sys 等等。0 是不要检验,1 表示最早检验(根目录配置为 1),2 表示要检验

options 常用参数:

  • defaults 使用默认设置,rw,suid,dev,exec,auto,nouser,async
  • auto noauto 自动和手动挂载
  • ro rw 只读和读写挂载
  • exec noexec 二进制文件是否允许执行
  • sync async I/O 同步,I/O 非同步
  • user 允许任何用户挂载设备,nouser 只允许 root 用户挂载

系统挂载的限制:

  • 根目录 / 必须先于其他设备挂载
  • 挂载点必须为已经创建的目录
  • 挂载点在同一时间只能挂载一次
  • 分区在同一时间只能挂载一次

2019-02-18 fstab , mount , linux , file , disk

设计模式之结构模式

设计模式中的结构模式可以让我们组合类或者对象形成比较大型的结构,但也能保证结构的可维护性和可扩展性。

适配器模式

适用场景:

  • 复用一个已经存在的类,但是类提供的接口不符合使用场景,使用适配器重新定义接口
  • 在不对每一个都进行子类化以匹配它们的接口的情况下,使用一些已经存在的子类

类适配器

类适配器把”被适配的类的 API” 转换成 “目标类的 API”。

三个角色:目标 (Target),源 (Adaptee) 和适配器 (Adapter)

对象适配器

与类适配器一样,对象适配器也是把”被适配的类的 API” 转换成 “目标类的 API”。不同的是,类适配器中”Adapter 和 Adaptee 是继承关系”,而对象适配器中”Adapter 和 Adaptee 是关联关系 (Adapter 中存在 Adaptee 类型的成员变量)”。

适配器模式的缺点:

  • 大量使用适配器容易造成系统结构混乱

合成模式

又称为“部分 - 整体模式”,合成模式将对象组织到树结构中,可以用来描述整体与部分的关系。

装饰模式

装饰 (Decorator) 模式又名包装 (Wrapper) 模式。装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。

它以对客户透明的方式动态地给一个对象附加上更多的责任。换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同。装饰模式可以在不使用创造更多子类的情况下,将对象的功能加以扩展。

Decorator 模式重点在于责任,在于保持对外接口一致的情况下扩展接口功能。这是符合开闭原则的一个模式。

代理模式

代理模式给某个对象提供代理,由代理对象控制原对象的引用。

代理模式包含:抽象主题,代理主题,真实主题

  • 抽象主题,声明共同接口
  • 真实主题,真正内容
  • 代理主题,包含一个真实主题,传递调用

注意点:

  • 代理模式并不改变接口,代理模式的用意是让客户端不用在意代理的存在
  • 代理类将对接口的调用转发给真实的调用,代理起到一个传递请求的作用,同时在传递请求时可以额外的添加特定操作

享元模式

对象结构模式,以共享的方式高效地支持大量的细粒度对象,通过共享来避免大量拥有相同内容对象的开销。

享元模式中的对象称为享元对象,享元对象分为内蕴状态和外蕴状态。内蕴对象和外蕴对象是相互独立的:内蕴状态是存储在享元对象内部,并且不会随环境改变而有所不同的;内蕴状态是可以共享。外蕴状态是随环境改变而改变,不可以共享的状态;享云对象的外蕴状态必须由客户端保存,并在享元对象被创建之后,在需要使用的时候再传入到享云对象内部。

Java 中享元模式的一个典型应用就是 String 类的实现,假设

String a="hello";
String b="hello";

变量 a 和 b 都被赋值 “hello” 那么这个字面常量会存在常量池中,实际上是同一个对象。

享元模式分为”单纯享元模式” 和 “复合享元模式”。

单纯享元模式

单纯享元模式涉及到的角色:抽象享元 (Flyweight),具体享元 (ConcreteFlyweight),享元工厂 (FlyweightFactory)。

复合享元模式

  • 复合享元由单纯享元复合而成
  • 复合享元实现抽象享元定义的接口

门面模式

外部与一个子系统通过统一的门面对象进行交互,门面模式提供一个高层次的接口,使得子系统更加容易使用。

比如说一个病人去医院(子系统)看病,那么需要挂号,门诊,收费,取药等等步骤,如果引入门面模式,相当于在医院增加一个引导员,由引导员负责挂号,门诊,收费,取药等等,病人只需要与引导员打交道。那么这样依赖病人就会方便很多。

门面系统由门面和子系统两个部分。

桥梁模式

桥梁模式是对象的结构模式,桥梁模式的用意是“将抽象化 (Abstraction) 与实现化 (Implementation) 解耦合,使得二者可以独立地变化”。

一般情况下抽象化类中包含一个具体实现,通过包含关系将强关联解耦。

桥梁模式中的三个关键词:

  • 抽象化,抽取出共同的、本质性的特征
  • 实现,抽象化的具体实现
  • 解耦合,将抽象化和具体实现之前的强耦合关系改为弱关联;所谓强关联,就是编译时已经确定的关系,比如继承关系;而弱关联则是可以动态的确定并且可以在运行时动态改变的关系,比如聚合关系

优点:

  • 分离了抽象和实现,给扩展提供了灵活性
  • 桥梁模式可以分别独立的扩展抽象部分和实现部分

桥梁模式一个非常典型的使用就是 JDBC 驱动器,JDBC 为所有关系型数据提供了通用界面,一个应用选择一个合适的驱动器,通过驱动器向数据库引擎发出指令,这个过程就是将抽象角色的行为委派给具体实现角色的过程。

因为 JDBC 驱动器的存在,应用系统可以不依赖数据库引擎细节而独立发展,而同时数据库引擎也可以独立于应用系统的细节而独立发展。

jdbc driver

JDBC 这种架构,将抽象部分和具体部分分离,对于应用程序而言,只要选用不同的驱动器,就可以让程序操作不同的数据库,而无需更改应用程序,从而实现在不同的数据库中的移植。对于驱动器程序而言,为不同数据库实现不同的驱动程序,并不会影响应用程序。

reference


2019-02-14 design-pattern , java , object-orientation

设计模式之创建模式

本文主要归纳设计模式中的创建模式 (creational pattern),主要包括了工厂模式,单例多例,建造者模式,和原型模式等。

创建模式是指对象的实例化过程,这些模式都提供了一种将对象实例化从客户端分离的方法。

为什么要有创建模式,这也符合开闭原则,Java 自带的 new Object() 实例化对象没有任何问题,问题在于修改,一旦当具体实例涉及变化,那么就必须修改实例自身,不符合开闭原则,所以才有这么多的创建模式。将对外暴露的接口抽象起来,将对象创建的方式封装,对外接口尽量减少。

简单工厂

主要分为三个部分,工厂,抽象产品,具体产品。

工厂直接提供具体产品,也就是直接创建具体类。

工厂模式将产品的生产和其提供的功能隔离。

多态工厂方法

又称为工厂方法。

The Factory Method Pattern defines an interface for creating an object, but lets subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

分为四个部分,抽象工厂,具体工厂,抽象产品,具体产品。将产品实例化放到具体工厂子类中实现,抽象出工厂的一般方法。通常一个具体工厂产出一种具体产品。

因此在添加具体产品后不需要修改具体工厂类,而只需要相应的添加具体工厂来产出产品。

抽象工厂

和多态工厂相同,包含四个部分。

The Abstract Factory Pattern provides an interface for creating families of related or dependent objects without specifying their concrete classes.

抽象工厂工厂方法的区别在于,抽象工厂模式中,一个工厂可以提供多个抽象产品。在工厂方法模式中,由“具体工厂”决定提供哪一类具体产品;在抽象工厂中,由客户端决定返回哪一类产品。

单例

至始至终只有一个实例。

多例

一个类存在多个自身的实例,并且多例类需要自己创建,管理自己的实例,并向外提供自己的实例。

常见的线程池,数据库连接池就是多例的使用案例。

建造者模式

当需要创建的对象,创建步骤比较复杂时,可以将对象的”创建”和”表示”分离。Builder 模式允许用户将复杂对象的创建封装,允许使用者多个步骤来构造对象。

建造者模式包括:导演,抽象建造者,具体建造者,和产品

建造者模式的使用场景:

  • 对象内部结构复杂,比如包含不同零件的汽车类,比如一个复杂的邮件类
  • 对象内部属性有依赖,需要按照某一些顺序进行赋值
  • 对象创建过程依赖系统其他对象,而这些依赖的对象不易获取

优点:

  • 封装了内部构造细节
  • 隐藏了内部表示
  • 构造对象内部实现可以灵活改动,客户端只通过抽象接口来构造该对象

缺点:

  • 相较于工厂模式,客户端需要额外的知识来创建对象

原型模式

通过原型指明要创建的对象类型,然后通过该原型创造更多的对象实例。Java 中可以通过 clone() ,或者反序列化来实现。原型模式允许客户端在不知道对象具体内部细节的情况下创建实例。

一个比较具体的使用场景,比如要对比两个对象修改前后的差异,可以在修改前复制一个对象实例,然后修改之后对比。再比如一次比较重的数据库查询,可以缓存该对象,再下一次请求到来时直接返回该对象的复制。

一些比较适合的场景:

  • 当创建一个实例比较昂贵或者复杂时

原型模式的优点:

  • 对客户端隐藏了构造对象的复杂细节
  • 对客户端提供了一种在不确定类型情况下创建对象实例的方法
  • 某一些场景下复制对象实例要比创建来的更有效率

缺点:

  • 对于一些只有少量对象的系统来说并不太需要
  • 每一个原型类的子类都需要实现 clone 方法,有些时候创建对象拷贝并不是非常轻松

reference


2019-02-13 design-pattern , factory , singleton , builder

Kindle 篇一:如何把 Kindle Unlimited 的钱看回来

阅读专题书籍

Kindle Unlimited 特别适合专题阅读,比如想要了解日本文化,那么通过搜索日本,可以获得一系列的关于日本的书籍,借阅自己喜欢的书阅读即可。比如日本就可以搜索到,《静观日本》,《牛津通识读本现代日本》,《日本人的活法》,《走遍日本》,《图说日本》,《日本论》,《日本常识》等等的书籍,如果变换关键词也会搜索到像《菊与刀》这样的关于日本文化的书籍。那么通读这一系列的图书就能够快速建立起对于日本这个国家的认识。 同样的道理如果想要了解,政治学,心理学,可以直接以关键词去搜索,当然不一定这一个门类的图书都是在书名中包含关键字的,但是 Kindle Unlimited 的一个好处就是可以快速浏览目录,再去判断要不要去精读这本书。

善用豆瓣

豆瓣之于我,更像是一个工具,一个长周期的,管理信息的 Todo list,我一般会在豆瓣提前标注好想看的书,然后按照自己的喜欢程度来依次阅读。所以一般不会有书荒,也不会因为想看的书太多而造成压迫感。

制定读书计划

如果一个月看不到 3 本书,确实没必要买 Kindle Unlimited 了。所以这就体现了计划的重要性,除了上面提及的专题阅读,以及豆瓣的规划,最好在用豆瓣的豆列来指定当月或者当季度想看的书单。

看自己喜欢看的书籍

看自己喜欢的书,虽然听起来很简单,但很多时候其实很难实现,往往有些时候在书单中的书这个时候看非常晦涩,而在另外一个时候再回来读却能读出不一样的东西。很多书可能很长时间躺在书单中,但是却一直没有找到阅读的理由,但是往往可能因为别人的一句话或者看过的一部电影而重新从书单中被找出来,然后获得更多的体验。读某个阶段自己最关心的一个话题的书籍,或者某一个阶段最喜欢的一类图书即可。大可直接拿起一些图书,然后略读一个大概然后记住,等到真正关心起这段内容再仔细阅读。

不要一次性借阅太多的图书

把借书还书当成一次仪式,Kindle Unlimited 一次也只能够借阅 10 本,所以没必要同时阅读超过 2 两本书。一本本看,看完一本做些笔记,写些感想。


2019-02-12 kindle , amazon-kindle , kindle-unlimited , books

Google Forms 使用

Google Forms 是集成在 Google Docs 中可以用来快速创建调查问卷,建立报名系统,反馈调查等等的应用。Google Forms 原来属于 Google Docs,现在已经集成在了 Google Drive 中。

打开入口

Google Forms 的入口藏的有些深,如果不是之前就知道这个功能可能很多人用了多年 Google Docs 或者 Google Drive 都不曾知道有这个功能。在 Google Drive 中左上新建,然后在菜单中找到更多,然后其中有 Google Forms。

另外可以直接打开 Google Forms 的二级域名,也会跳转到对应的页面。

创建及修改

创建和修改部分其实非常简单,UI 也非常清晰。不过这里可以总结 Google Forms 支持的问题及答案类型:

  • 简短答案,可以用来回答比如姓名,电话,电子邮件等等简单的文本,甚至可以用正则来验证
  • 段落,比较长的文本
  • 选择题
  • 多选题
  • 下拉列表,可以根据不同的选项转到不同的小节
  • 上传文件
  • 线性量表,会使用单选形式出现多个选项,可以用来设计评分等等
  • 选择题网格,通常是每一行一问题,而每列中是不同的选择
  • 日期
  • 时间

通过组合这些功能能够设计出一个完整的调查问卷

收集结果

在 Forms 中能够直接查看回复,并且可以关联 Google Sheets,然后将结果在一张表中展示出来。


2019-02-01 google , google-docs , google-drive , quiz

expect 脚本使用

expect 是用来进行自动化控制和测试的工具。主要是和交互式软件 telnet ftp passwd fsck rlogin ssh tip 等进行自动化的交互。Linux 交互命令中经常需要输入 yes/no 或者 password 等操作,模拟这些输入,就可以使用 expect 脚本。expect 是由 tcl 语言演变而来的。简单地说,expect 是一个工具,可以根据用户设定的规则和系统进程进行自动化交互,例如远程登陆的密码输入、自动化的执行远程命令。

安装

Debian/Ubuntu/Linux Mint 系安装非常简单

apt install expect

关键命令

expect 下几个非常重要的指令:

  • spawn: 启动进程(由 spawn 启动的进程的输出可以被 expect 所捕获)
  • expect: 从进程接收字符串,期望获得字符串
  • send: 向进程发送字符串,用于模拟用户的输入,注意一定要加 \r 回车
  • interact: 用户交互

spawn 指令用来开启比如 Shell, FTP, SSH ,SCP 等等的交互指令。

命令行参数

$argc,$argv 0,$argv 1 ... $argv n

argc 表示命令行参数个数,后面分别表示各个参数项,0 表示第一个参数,1 表示第二个参数,以此类推,可以通过 lindex 获取对应参数值 (lindex $argv 0)。

if {$argc < 2} {
    puts stdout "$argv0 err params\n"
    exit 1
}

if {[llength $argv] == 0} {
    puts stdout "need server name as param"
    exit 1
}

输入输出

puts stderr "Usage: $argv0 login passwaord.n "
puts "hello world"
puts stdout "1234"

变量赋值

set argv [lindex $argv 0]
set user "einverne"
set count 3
set ip "192.168.2.1"

复合指令

比如要将脚本的参数赋值给变量

set my_var [lindex $argv 0]

命令调用

spawn ssh $user@$ip

spawn 启动一个进程,进程执行 ssh 命令,程序后面可以通过 expect/send 和新起的进程进行交互

分支语句

单一分支语法:

expect "hello" {send "you said hello"}

多分支模式语法:

expect {
    "lilei" {send "hello lilei"; exp_continue}
    "hanmeimei" {send "hello hanmeimei"; exp_continue}
    "how do you do ?" {send "how do you do ?"}
}

switch 分支

switch -- $var {
{
  }
{
  }
{
  }
}

if else 分支

set NUM 1
if { $NUM < 5 } {
    puts "\Smaller than 5\n"
} elseif { $NUM > 5 } {
    puts "\Bigger than 5\n"
} else {
    puts "\Equals 5\n"
}

循环

while 循环语法

#!/usr/bin/expect -f
set NUM 0
while { $NUM <= 5 } {
    puts "Number is $NUM"
    set NUM [ expr $NUM + 1 ]
}

for 循环

for {set NUM 0} {$NUM <= 5} {incr NUM} {
    puts "\nNUM = $NUM"
}
puts ""

自定义方法

定义

proc myfunc { TOTAL } {
    set TOTAL [expr $TOTAL + 1]
    return "$TOTAL"
}

使用

set NUM [myfunc $NUM]

使用正则表达式判断

if {[regexp {^[0-9]+$} $NUM]} {
  Do something
} else {
  Exit
}

其他表示

if {![regexp {\D+} $NUM]}
if {![string match {[^0-9]+} $NUM]}

实例

登录远程服务器并创建文件夹

#!/usr/bin/expect               // 告诉系统脚本的执行方式
set timeout -1                  // 等待超时时间, -1 为无限制等待
spawn ssh root@192.168.2.1      // spawn 执行 expect 内部命令,给 ssh 运行进程加壳,传递交互指令
expect {                        // expect 是 expect 内部命令,判断上次输出结果是否包含,如果有则立即执行操作
    "password" {send "123456\r";}           // send 执行交互动作,与手工输入密码等效
    "yes/no" {send "yes\r";exp_continue}
}
expect "root" {send "mkdir testFolder\r"}
expect eof
exit

expect eof 等待结束标志,spawn 启动的命令在结束时会产生一个 eof 标志。

带参数的脚本

如果脚本依赖外部输入,比如有输入参数,那么可以在后面添加参数:

./expect.ex 192.168.2.1 123456

脚本可以首先用 set 给变量赋值

#!/usr/bin/expect

set ip [lindex $argv 0]
set password [lindex $argv 1]
set timeout -1
spawn ssh root@ip
expect {
    "password" {send "$password\r";}
    "yes/no" {send "yes\r";exp_continue}
}
expect "root" {send "mkdir test1\r"}
send "exit\r"
expect eof
exit

等待手动操作

登录远程服务器之后使用 interact 来等待手动操作,比如:

./expect.ex 192.168.2.1 123456

脚本:

#!/usr/bin/expect
set ip [lindex $argv 0]
set password [lindex $argv 1]
set timeout -1
spawn ssh root@$ip
expect {
    "password" {send "$password\r";}
    "yes/no" {send "yes\r";exp_continue}
}
interact                // 完成后保持交互状态,把控制权交给控制台

注意最后的 interact

总结

expect 在 Linux 运维中非常有用,可以用来多机器重启,远程 copy 等等场景,shell 中受限于密码输入的操作,可以通过 expect 来节省很多工作。

涉及到脚本地址

reference


2019-01-29 expect , linux , automate , shell , script , 运维 , 自动化

电子书

最近文章

  • Vim 插件之全局搜索:ack.vim 这篇文章看开始陆陆续续记录一下用过的 Vim Plugin,虽然有些一直也在用但从没有好好整理过,正好这篇开一个计划吧。
  • 我可能要抛弃用了很多年的 Chrome 换用 Vivaldi 大概一两年前就听说了 Vivaldi 这样一款浏览器,它使用 Chromium 做内核,创始人是从 Opera 浏览器出来的,不满 Chrome 横扫浏览器市场,没有给 Opera 一点喘息的机会,很多 Opera 的特性在 Chrome 上都被摘掉了。所以他们就开始搞了这一个 Vivaldi 浏览器,我记得当时听到的第一个宣传语是,快,想来 Chrome 最初打开市场时的宣传语也是快,但是多少年过去了 Chrome 虽然自身保持非常干净,但是加上自定义的扩展,也变得越来越臃肿了,但倒是也在一个可以接受的范围内。两年前 Vivaldi 依靠这一条宣传没有吸引到我,但是我也下载尝试,并且这两年来一直存在我的电脑中。我记得之前在我看来最大的缺点便是不能同步数据,而这个功能也在之前的更新中被加上了。所以今天就非常值得拿出来讲一讲。
  • 使用 port knocking 隐藏 SSH daemon 端口 暴露在互联网上的服务器非常容易被恶意程序进行端口扫描,以前也整理过一篇 VPS 安全设置 的文章,但都是一些比较基础的设置,能够绕过一些简单的端口扫描,但是并不能从根本上解决端口扫描的问题。
  • 威联通折腾篇十四:迁移系统盘 当时安装系统的时候就直接插入了一块硬盘,安装在了第一块机械硬盘上面,虽然读写也没有遇到什么瓶颈,但是记录以做备份,可以用于将系统迁移到 SSD 上。
  • Intellij IDEA 支持 jQuery 在 设置中进行如下操作开启 jQuery 支持: