Spring Test的一些小技巧

目前项目在使用Spring Test进行单元测试(其实算是集成测试了),有两个小知识想和大家分享下。

1.对于同时存在注解、xml配置的bean会以xml中的配置为准

例如spring配置文件如下:

    <!-- application.xml -->
    <context:component-scan base-package="com.itechlib.com.service" />
    
    <!-- 在测试目录下定义stub类 -->
    <bean id="userDao" class="com.itechlib.com.test.stub.UserDaoStubServiceImpl" />

Java代码如下:

@component("userService")
class UserServiceImpl implements IUserService {

@Resource(name="userDao")
private IUserDao userDao;

  public void doUpdate(){
    //...
    userDao.update();
    //...
  }
}

这样在测试时UserServiceImpl调用的就是stub包下定义的userDao的模拟类了。

2.同时运行多个测试类的时候,可以有不同的ContextConfiguration配置

例如使用Junit TestSuite来一次执行多个测试类,这时如果不同测试类的ContextConfiguration配置不一样会怎么样呢?其实这些测试类都能够正确运行,因为spring会针对不同的configuration创建不同的ApplicationContext,而且这些ApplicationContext都会进行缓存,只会加载一次。

下面这些key不同就会生成不同的ApplicationContext,具体见spring reference 文档 Context caching

  • locations (from @ContextConfiguration)
  • classes (from @ContextConfiguration)
  • contextInitializerClasses (from @ContextConfiguration)
  • contextLoader (from @ContextConfiguration)
  • activeProfiles (from @ActiveProfiles)
  • resourceBasePath (from @WebAppConfiguration)

如果测试中破坏了ApplicationContext,可以对这个测试类声明@DirtiesContext

例如下面的写法会生成两个独立的ApplicationContext。

@ContextConfiguration(loader = SpringBeanTestContextLoader.class, locations = {
    "classpath:test-resources/application/application.xml"})
class UserServiceTests extends AbstractJunit4SpringContextTests{
    @Test
    public void testUpdate(){
    //...    
    }
}

@ContextConfiguration(loader = SpringBeanTestContextLoader.class, locations = {
    "classpath:test-resources/application/application-custom.xml"})
class OrderServiceTests extends AbstractJunit4SpringContextTests{
    @Test
    public void testAdd(){
    //...    
    }
}

了解这些你可以更好的规划你的测试配置,更好地测试各种场景和分支。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《ITechLib》

应用系统无事务时如何保证交易一致性

在进行应用系统开发时一笔交易往往需要多次操作数据库,为了保证交易的一致性、正确性,简单的方式是通过数据库事务来控制,多次数据库操作要么均成功要么均失败。但有时我们不能将多次数据库操作封装在一个事务中,主要有两方面原因:1.多次数据库操作中间需要调用外部服务,而这个外部服务比较耗时,此时如果统一封装在一个事务中会长时间占据数据库连接,可能导致长时间锁定某些资源,从而严重影响系统并发度;2.随着异构系统越来越流行,一个系统可能同时有多个数据库,或是同时有关系数据库、Nosql数据库、分布式缓存、消息队列等,如何保证这些系统之间的一致性,可以使用“两阶段提交”或PAXOS等分布式协议,这样的话会显著增加系统的延时,降低系统的吞吐量,另外当有异常时处理也很复杂,会有超时、死锁等问题。

其实对于应用系统来说,如果不是要求“实时一致性”,只是要求“最终一致性”,是可以通过系统设计做到不使用两阶段协议也可以保证一致性的(参考Idempotency Patterns)。下面先表述几个概念:

  • 一致性:其实就是同一系统或多个系统对于某个数据状态的一致意见,这也包括从外部调用者来看状态是一致的。在数据库系统中只要记录了提交日志,事务就相当于成功了。这里日志就相当于合同契约,所以日志必须持久化,否则就没有意义了。在分布式系统协议PAXOS中其实多个系统最终也是得到了一份契约,所以PAXOS也可以作为类似两阶段协议这样使用。
  • 最终一致性:系统在运行中或发生故障时处于某个“临时状态”,经过一段时间后会重新进入到一个“一致状态”。注意这里的“临时状态”并不是错误状态,只是说它是一个中间状态,这个中间状态也可以是正确的,只是不是最终状态而已。如果能做到这个中间状态对于客户是透明的就更完美了(数据库系统就做到了)。这里不提什么“弱一致性”,弱一致性其实就是不保证一致性,这只是业务对于系统设计问题的容忍,如果你是做账务系统,就不要考虑弱一致性了。

其实很多系统仅要求“最终一致性”即可,如果系统可以做到正常情况下完全一致,异常的情况下一段时间(如5分钟内)做到交易结果一致,客户应该都没什么问题。时间也是我们的朋友,虽然这个朋友在多个机器上不完全一致,但是误差都很小,在系统设计时可以加以利用。

下面主要介绍在不使用事务的情况下如何保证系统正确性,对于如何使用“两阶段提交”(如JAVA XA规范的分布式事务)可以参考其他文章(推荐这篇)。我们这里主要依靠下面几个工具:

  1. 状态:当前交易所处的状态,状态要尽量少、明确,要以事务边界为粒度。状态应避免循环,尽量采用顺序前进的方式。对于需要重复或是循环时可以设置子状态,以保证业务逻辑清晰。
  2. 自动任务:对于中间状态的记录进行处理,使其恢复正常状态。此任务应该考虑时间,防止过早调起,将正常交易作为中间交易进行处理。
  3. 时间:要记录数据的最后更新时间,通过时间确定记录是真的异常还是正常处理中。不同的系统的时间间隔应该不同,具体根据业务需要和技术实现来定。
  4. 可测试性/幂等性(重发):可以参考我的另一个文章《系统重发设计》。

系统内部设计

下面先介绍单个系统内的处理,假设一个系统涉及如下处理逻辑:

  1. 系统收到交易请求,进行交易输入合法性检查,如检查失败直接报错。
  2. 业务合法性检查,如合法性检查失败则记录交易失败信息。
  3. 调用外系统交易进行信息登记。(可能会有异常)
  4. 系统内部进行限额扣减。
  5. 调用外部系统进行转账处理。
  6. 记录转账结果
  7. 如转账失败则回冲限额(这里为简单起见,假设前面的信息登记动作由其他自动任务保证一致性,否则此处需增加外呼冲正)
  8. 返回交易结果。

针对上面的场景应该如何设计系统呢,最简单的设计是把所有这些全部放在一个事务内,由事务保证一致性,这样的话事务很长,如果遇到外呼超时会严重影响系统性能。如果采用状态来控制应该采用下面步骤:

  1. 定义状态,根据前面的状态规范,尽量少、明确,以外部系统为边界。可以看出可以分为如下状态:1-待登记;2-处理中(登记完成,限额扣减完成,待转账);3-交易成功;4-交易失败。
  2. 确定合适的记录数据库时机,延迟保存原则,只在状态需要变更时保存流水,且预计流水(参考数据库的先记日志原则)。系统内尽量通过事务保证一致性。
  3. 定义自动任务,对各种中间状态(此处为1-待登记;2-处理中)进行处理,通过重发或是查询外系统的结果来确定后续做法。需要和对方系统约定处理方式。

下面展示下设计后的系统处理模式:

  1. 系统收到交易请求,进行交易输入合法性检查,如检查失败直接报错。(无DB操作)
  2. 业务合法性检查,如合法性检查失败则记录交易失败信息。(若失败记录4-交易失败)
  3. 调用外系统交易进行信息登记。(可能会有异常)(外呼前记录1-待登记) (外呼后若失败,则记录4-交易失败)
  4. 系统内部进行限额扣减。(扣减限额与更新流水放在一个事务中,状态改为2-处理中)
  5. 调用外部系统进行转账处理。 (外呼后若失败,则记录4-交易失败)
  6. 记录转账结果(若5成功则更新状态为3-交易成功)
  7. 如转账失败则回冲限额 (若5失败,则记录4-交易失败,同时回冲限额,放在一个事务中)
  8. 返回交易结果。

对于上面的状态如果想保存中间结果,例如登记成功的结果、外呼失败的信息,可以通过子状态来做,或是增加状态。但这样会增加自动任务处理的复杂度,需要做更多的判断,好处是可以一定程度减少外呼查询或其他动作。如果系统内部的多数据库操作不想使用事务封装或是无法封装(可能涉及多个模块),也可以通过状态区分开各个阶段。

系统间设计规范

多个系统内一致性控制需要采用分段控制。即每个系统都要保证自身的一致性、正确性。调用方系统保证消息被正确传递,并保证最终更新交易结果;被调用方保证自身的一致性,实现自身状态可测试性或是幂等性。系统之间的消息,采用唯一标识区分,被调用系统提供查询消息结果接口,或支持幂等性(可重发)。此唯一标识由调用方生成,调用方系统在调用外呼系统前记录此唯一标识,被调用方通过此唯一标识实现交易幂等性。如果被调用方支持多个调用方,可以通过调用方编号+唯一标识来作为唯一标识,或是指定统一的标识生成规则,由各方遵守。

如果存在多个系统串联的情况,可以通过这种方式来实现正确性传递。注意这种传递只能是从前->后,而不能由后面的步骤保证正确性,因为后面方法可能会没收到消息。

本文所述方法可以从系统设计层面保证交易的正确性,可简化记为:状态 + 自动任务 + 时间 + 可测性/幂等性 = 最终一致性。当然了这种方法也有弊端,1.应用设计需要考虑各种状态转移,系统在记录状态同时,需要记录时间、唯一标识等额外信息,增加系统复杂度。2.自动任务需要详细设计,通过时间保证和联机交易不同时执行,另外需通过乐观锁保证状态仅被自动任务更新(建议update时进行原状态对比)。3.后一步结果严格依赖前面的执行结果,只能采用串行模式。4.需保证中间状态对于客户是无害的,包括查询结果和实际业务效果(这一点有时很难做到)。

本文为我个人的一点经验总结,希望能对大家设计系统有帮助,大家有更好方式也欢迎指出。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《ITechLib》

系统重发设计

重发又称为幂等性(Idempotency,即f(x) = f(f(x))),是指重复发送后会得到和和首次发送相同的结果,且不会导致不良的结果。重发是一个很重要的系统特性,数据库系统的事务设计、系统异常的处理都依赖对重发的支持。重发说起来简单,但做起来可不简单,有些交易本身天然支持重发,例如将所有记录状态改为正常,重复做一次并没有什么问题。但有些交易支持重发则很困难,例如向A转账10元,如果重做一次就可能会重复入账。

对于不是天然支持重发的交易,如果要支持可重发,需要做一些特殊处理。简单的方法是通过唯一标识来判重,具体步骤如下:

  1. 发送方生成消息的唯一标识,在发送消息中附上此标识;
  2. 接收系统收到消息在处理时,在完成消息处理的同时进行唯一标识的判重。例如记录流水,在流水中包括此标识,并对此标识通过唯一索引来判重;
  3. 对于消息重复的情况,给客户端反馈特定的错误码、错误信息,接收方以此得知消息已重复。交易结果建议反馈“处理中”,以免客户认为交易失败。

如果流水信息中不便于添加此标识,可以建立一个辅助表,用于存储此标识,通过唯一索引判重。记录辅助表和更新其他信息尽量放在一个事务中以免双方记录不一致。由于判重一般只要保证一段时间即可,故此辅助表的数据不必保留太久。

此处记录辅助表和记录流水放在一个事务中是为了保证一致性,如果不采用事务保证,建议先记录判重辅助表,这时可能会出现记录了辅助表,但未记录流水的情况(可能记录流水时出错,或是记录流水前出错),这时如果再有相同消息进来仍然会报错。这种程度的误报你需要分析业务上是否有问题,这种情况下你的系统响应是否正确。不建议在记录流水后再记录判重表,这时数据库中的流水记录无法回滚,这时客户来查消息结果会得到两条流水,很难区分哪个是正确的结果。

对于系统交易判重不建议采用查询方式,即在记录流水前查询一下流水是否存在,因为会有并发问题,之前生产上也遇到过,当两笔交易几乎同时发送时就会出现判重规则失效,记录重复流水的问题。需要指出,这并不是小概率事件,而是设计缺陷。

系统正确性的保证依赖与系统两方面的能力支持:1.可测试性,即系统支持调用方查询交易结果。2.可重发,且不会因为重复执行造成不良影响,即幂等性。系统只要支持其中一种就行,如果一种都不支持,永远无法保证操作正确性。其实很多实操做就是如此,例如你需要向水中扔1个石头,如果你正巧扔的时候精神溜号了,等你回过神发现水面很平,你不知道是否扔进去了,你现在再扔1个可能多扔了,如果不扔可能前面1个没扔进去。对于这种实操作,必须在执行前深重考虑,确保精神不要溜号,哈哈。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《ITechLib》

账务交易一致性设计概述

对于账务交易最重要的就是交易一致性,即给客户反馈正确的一致的结果。交易一致性并不是一个简单的话题,说来话长了。下面我结合项目的一些经验,谈一下我认为重要的地方。

交易本身大体分为三类,联机交易、批量交易、批量联机交易(即联机实时返回多笔交易的结果)。下面分别对于不同的场景进行说明:

1 联机交易

1.1 接口设计

从接口设计角度,对于联机交易一般包括两个接口,账务发起接口、交易结果查询接口。其中联机交易接口用于发起实时交易,并实时返回结果。交易查询交易则用于对账使用,用于确认账务成功与否。

账务发起接口:

此接口输入至少应包括如下字段:客户标识、客户交易流水号(用于客户对账使用)、其他账务相关的要素包括付方账号、收方账号、金额等(具体根据业务需要来定)。

输出至少应包括如下字段:交易结果代码、错误码、错误信息、系统交易流水号(可选)、交易日期(可选)。交易结果代码包括成功、失败、不确定三种状态,其中当交易超时等异常时反馈不确定状态,由客户调用“交易结果查询”接口进行对账。另外两个可选字段系统交易流水号用于表示本系统内部的交易流水号,方便排查问题;交易日期表示交易发生的记账日期,以便客户知道这笔账属于哪日的交易,如失败“交易日期”可以为空。接口务必保证交易结果代码的正确性。

交易结果查询接口:

此接口仅用于账务交易反馈不确定或超时时对账使用,对于需要列表查询的需求建议另外使用单独接口来做(例如支持分页,支持模糊查询等),这主要是出于性能、可维护性如流控等方便的考虑。对于交易结果查询接口输入字段应包括:客户交易流水号、系统交易流水号(两者二选一输入)、客户标识,输出字段除了包括账务发起接口的交易结果输出字段外,还应该包括查询结果代码、查询错误码、查询错误信息,用于反馈这笔查询交易是否查询成功,是否应该后续继续查询。对于同时包括报文头、报文体的交易,查询结果代码等可以放在报文头中,而交易本身结果可以放在报文体中。

1.2 其他说明

上面提到的客户交易流水号由客户生成并上传,生成规则应保证同一个客户内部全局唯一,以便用于对账。注意不要采用一天内唯一的概念,由于时间并不是一个双方都认可的值,特别是涉及到日切时。可以采用一周内、一个月内唯一的概念,这需要在接口中提前约定。此客户交易流水号涉及到判重的问题,系统应保证交易唯一性,建议通过数据库唯一索引来做(具体实施方式将在后续文章介绍)。

系统交易流水号由我方系统生成,应保持全局唯一性,此流水号建议包含日期、时间信息,以便排查问题。

交易结果代码:由于此代码十分重要,程序务必保证其正确性。应用应将此字段放在应用能够完全控制的地方,例如对于报文包括报文头、报文体两部分,而报文头由框架负责填充,此时建议将此字段连同错误码、错误信息一起放在报文体中,对于报文头中的错误和客户约定为”处理中”状态,这是为了避免由于框架原因而导致原本正确的交易返回失败。此规则适用于账务发起接口及交易结果查询接口。

如需提供交易结果列表查询功能,应发布单独接口。此接口应支持分页查询、或是查询下一页功能(可通过在输入中增加上次查询最大系统流水号字段实现),支持按账号、按金额、按日期等进行筛选。注意此处日期包括:客户提交日期、系统营业日期、记账日期,这些日期概念是不同的,由于此接口仅用于查询不用于对账,如果业务允许可以仅支持一种,建议使用客户提交日期。

1.3 交易超时设置

交易在每个外呼中均应该设置超时时间,超时时间应该满足“漏斗原则”,防止在后端未处理完成时前端已经失败返回。漏斗原则是指从客户前端->中间转发服务器->应用服务器->账务核心服务器,各个节点的超时时间应该以此减少。对于特殊情况不能满足漏斗原则的情况,应该由应用服务器在程序逻辑上保证交易一致性,例如返回处理中并提供异步对账功能,或是提供冲正功能。若采用冲正方式,违反漏斗原则会造成很多交易结果不确定,交易应该在设计阶段考虑各种情况下超时如何保证交易一致性(冲正比较复杂,在此不做详细介绍)。

交易超时还包括一点:避免异常情况下查询交易早于账务发起交易到达。这一方面可以通过控制查询交易和账务发起交易的间隔来保证,例如约定超时时的查询间隔30s,但即使这样在特殊场景也会有问题。为了防止此问题可以在交易报文中设置交易发起时间、交易剩余处理时间,这两个字段在进入我系统后由前端系统生成并赋值(不能由客户填充),如客户通过外围系统接入此字段由外围系统填充。交易剩余时间一般设置为30s,在消息传递的各个环节应该对剩余时间进行重写(包括中间系统、应用系统等),如剩余时间为0,则不应该进一步传递消息,应直接返回失败。对于应用系统(即需要保证交易正确性的系统)在记录流水前也应该判断此剩余时间,如为0则应直接返回失败而不记录流水。

应用设计者应了解每只账务交易的一致性保证机制,从设计上保证一致性。对于账务交易尽量不要支持重发,重发很容易导致交易重复提交,且不容易识别查询返回结果,除非服务方和调用方系统进行周密的设计不建议采用。

2 批量交易

2.1 接口设计

批量交易是指客户通过上传批量文件的方式发起的账务交易,这种交易一般为异步处理。一般包括批量文件上传交易、批量结果查询交易。

批量文件上传交易

批量文件上传交易主要完成客户文件上传功能,系统如接收文件成功应返回客户文件接收成功。接口输入报文应包括客户标识、客户批次流水号(用于查询批量整批结果)、批量文件路径及名称、批量总笔数、批量总金额。输出字段和联机单笔交易类似,至少应包括如下字段:批量交易结果代码、错误码、错误信息、系统批次号(可选)、交易日期(可选)。

接口的其他注意事项包括交易结果代码、唯一性约束和联机接口一致。除此之外还有以下几点需要注意:

  1. 客户应保证批量文件早于联机报文到达,以便根据报文解析文件时文件已经发送完毕;
  2. 对于批量文件名称应保证唯一性,可以对于每个客户使用单独的目录。这一点需要和客户提前约定;
  3. 文件应使用特定分隔符分隔,需要保证分隔符和正常业务字段不重复,例如 @ # 等;
  4. 文件内每笔交易应包含客户交易流水号或批内子序号,用来查询结果时标识每笔明细;
  5. 系统对于批量的联机处理,至少应包括记录批量流水记录;对于后续处理如文件解析、校验,记录批量明细信息、业务逻辑校验,业务处理可以根据业务、性能需要考虑是否包括在联机处理中。
  6. 系统联机处理时应对每批文件的笔数进行控制,具体可以根据业务需要来,可以为200笔或2000笔。

批量结果查询交易

客户通过此交易可以查询批量处理的结果,包括两部分:一是批量整批的结果,一是每笔批量明细的结果。为了处理简单,可以当批量整批处理完成后再返回批量明细的结果,如果确实需要实时返回批量明细的结果,需要在设计时保证返回结果的正确性,包括明细的完整性、正确性(如批量太大不建议采用这种方式)。

输入接口应包括: 客户标识、客户批次流水号、系统批次号(可选);

输出接口应包括:1.整批信息:批量交易结果代码、错误码、错误信息、系统批量流水号(可选)、交易日期(可选);2.明细信息:客户交易流水号/批内子序号、明细交易结果代码、错误码、错误信息、系统批量子序号(可选)、交易日期(可选)。明细信息可以根据接口定义是否返回,如只有整批处理完再返回结果,可以定义为:当整批信息不为整批失败、处理中时会返回交易明细结果,当整批结果为交易成功时应返回明细结果。

这里需注意以下几点:

  1. 上述输出接口字段建议放在报文体中,通过报文头的系统交易结果、错误码、错误信息来表示查询交易本身的结果,如交易超时等;
  2. 如系统支持的批量文件较大时,需要通过文件来返回交易结果,返回报文中没有明细信息,但应该有明细结果文件路径及名称。文件名可以和客户输入文件名加后缀方式来对应。结果文件建议作为批处理的最后一步给客户提前生成好(建议在修改整批状态之前,以免客户查不到文件),并和客户约定保存多长时间。
  3. 为防止批量状态卡死,系统应该有专门的检测自动任务来检查异常的批量状态,并针对错误进行相应的处理(包括转人工处理)。
  4. 批量结果查询交易应和批量历史流水列表查询分开,以保证系统性能和可维护性。

2.2 其他说明

批量交易的其他注意事项,如判重、交易结果、超时,可参考联机单笔交易中的说明。在设计批量交易时应将整批作为一个整体考虑,结合系统非功能要求确定应支持的功能点,切记为了所谓业务需求,盲目实现不必要的功能。

3 批量联机交易

这类交易相当于系统提供的单笔交易的批量接口,但整批的结果要求同步返回(如不同步返回可以参考上面的批量交易)。这类交易比较特殊,为了保证交易一致性,需要将这多笔交易当做一笔来看待,作为整体记录流水,而不是简单的循环调多笔。批量联机交易一般不要直接发布给直连客户,可以作为界面或是内部调用的性能优化手段,如果没有性能问题应尽量采用循环单笔的方式。

输入接口应包含:客户标识、客户交易流水号[支持列表]、其他单笔交易的业务字段[支持列表]。输出接口应包含每笔交易的结果信息列表。对账接口应支持按照客户交易流水号列表来查询结果。

在交易设计时需要考虑如下几点:

  1. 对交易支持的最大笔数进行限制,这个限制的建议值为超时时间内能处理的交易笔数(包括启动多个并发、不启动并发两种情况),最大值为系统可以保证交易一致性的最大笔数。
  2. 交易处理方式可以考虑以下几种:a.将多笔交易作为一个整体提前记录流水信息,然后逐笔处理[建议];b.直接调用逐笔交易当做单笔处理,如已超时未处理记录直接返回失败,不做处理[可以满足一致性要求,但不建议];c.直接当做单笔处理,超时仅返回处理中,由判重功能来保证不会有账务问题。[不建议,客户无法知道应该等多久查结果、再次发送]
  3. 查询结果交易需要保证在交易处理的任何时点的结果都是正确的,不能仅查到部分交易的结果,故应该将多笔当做一个整体来预计流水。
  4. 多笔交易一般是有关联的,建议对中间数据进行交易级别缓存,以提高性能。
  5. 是否要采用并发处理建议能支持客户选择,由于并行处理对于某些场景是会影响交易正确性的。(PS.有些客户是在乎顺序的)
  6. 此交易会长时间占据联机服务器的线程,会大大降低系统的吞吐量,采用并发并不会使情况缓解。针对此交易设置单独限流策略。

上面对于三类交易的接口设计以及一些注意事项进行了介绍,后面文章再对账务交易的一些设计细节进行详细描述。

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《ITechLib》

sublime_terminal插件启动iTerm2(V3.0)问题修复

目前使用sublime来作为编辑器,经常需要切换到iTerm2终端来进行文件处理、执行命令。最近升级了iTerm2后发现sublime打不开终端了。在网上找了一圈发现iTerm2的3.0版本的脚本调用方式改了,一直未找到合适的解决方案,实在忍不了了自己动手修改了一下。现在分享一下,方便大家使用。

软件版本:

修复方法

打开sublime_terminal包的根目录,可以看到其中有一个iTerm.sh文件,这个就是启动iTerm的脚本,启动iTerm失败主要是此脚本的问题。可以直接使用下面脚本替换原文件中的内容:

#!/bin/bash

CD_CMD="cd "\\\"$(pwd)\\\"" && clear"
if echo "$SHELL" | grep -E "/fish$" &> /dev/null; then
  CD_CMD="cd "\\\"$(pwd)\\\""; and clear"
fi
VERSION=$(sw_vers -productVersion)
OPEN_IN_TAB=0

while [ "$1" != "" ]; do
  PARAM="$1"
  VALUE="$2"
  case "$PARAM" in
    --open-in-tab)
      OPEN_IN_TAB=1
      ;;
  esac
  shift
done

if (( $(expr $VERSION '<' 10.7) )); then
  RUNNING=$(osascript<<END
  tell application "System Events"
      count(processes whose name is "iTerm2")
  end tell
END
)
else
  RUNNING=1
fi

if (( ! $RUNNING )); then
  osascript<<END
  tell application "iTerm"
    activate
    tell current session of current window
      write text "$CD_CMD"
    end tell
  end tell
END
else
  if (( $OPEN_IN_TAB )); then
    osascript &>/dev/null <<EOF
    tell application "iTerm"
      activate
      tell current window
        select
        create tab with default profile
        tell current session of current tab
          write text "$CD_CMD"
        end tell
      end tell
    end tell
EOF
  else
    osascript &>/dev/null <<EOF
    tell application "iTerm"
      create window with default profile
      tell current session of current window
        write text "$CD_CMD"
      end tell
    end tell
EOF
  fi
fi

也可以新建一个iTerm2_3.sh的文件,此时需要修改sublime_terminal的配置Preferences > Package Settings > Terminal > Settings – User:

{
  "terminal": "iTerm.sh",
  "parameters": ["--open-in-tab"]
}

文章未经特殊标明皆为本人原创,未经许可不得用于任何商业用途,转载请保持完整性并注明来源链接 《ITechLib》