OSGiaaS-CLI — 可以用来运行 JVM 系语言 REPL 和命令的 CLI

crazymeercat 发布于1年前 阅读43099次
0 条评论

任何时候,和很多技术人员一样,我会需要运行一些 CLI(命令行界面)命令去执行一个快速,通常重复的任务。操作系统提供许多命令(如 ls,cat,cp,rm,grep ...),它们可以合并在一起以执行一些任务(像在亚马逊部署应用,或者是发现某些地方的最便宜的航班)。人们给他起了一个庄严的名字:Unix 哲学,这个方法能够用在任何操作系统中。

Unix 方式能提高你的操作效率,为此,很多人甚至认为 Unix 就是一个 IDE。

但是,很多时候,你会发现自己试图做一些不能轻易完成的事情,只需使用 CLI 中现有的工具。换句话说,你必须直接在 CLI 中写些内置代码,或者写你自己的命令。那么问题来了:在所有主要操作系统中,进行两者中的任一操作对 JVM 程序员来说都不是很方便。

对于更复杂的逻辑,你当然可以写 bash 脚本来实现,也可以直接用 C 或者  Rust 写本地可执行文件。但对于像我这样不喜欢(或没时间)学习新的编程环境的人,这并不是一个好建议。这类人会沉浸在 JVM 的世界,希望他们的程序可以不做任何改变就能用于任何操作系统。他们已经熟悉了丰富的 JVM 库,不需要再去尝试像 bash 或  awk 这些古老的语言。

这就是我创建 OSGiaaS-CLI 项目的原因。

OSGiaaS-CLI 项目把若干小型库集中在一起,基于 OSGi services  创建高度模块化 CLI ,可以像在 REPL 中那样运行代码片段。而 OSGi services 由 Java 或其它 JVM 语言实现。

在详细说明之前,先看一个使用 OSGiaaS-CLI 的示例(用 Java 8 Lambda 简单地处理 ls 命令输出的每一行):

>> run ls | java line -> line.contains("log") ? line : null

如果一个命令起始于 >> 提示符,表示这个命令是在 OSGiaaS-CLI 中输入的。如果没有 >>  提示符,则命令是 W 在 h 普通的 shell 环境中输入。

因为 Java 有点冗长,所以我通常使用更简洁的 Groovy 语法:

>> run ls | groovy if (it.contains("log")) it

我们都知道 ls 命令是用来干啥的(列出文件),然后会对 ls 输出的每一行运行 Groovy 脚本,在 it 包含 “log” 的时候返回它(返回的 对象 由 Java 或 Groovy 打印出来,就像其它语言的命名那样)。

完成上述任务最简单的方法是使用 grep: 运行 ls | grep log。

上面的例子只是简单地说明在 CLI 中使用 JVM 语言是件容易的事情。

我们可以使用任何由 OSGiaaS 工程支持的其他语言(当前支持:Java, Groovy, JavaScript ,处在实验中的是: FregeClojure 和  Scala ),并可以直接在 CLI 中运行。

为了证明这一点,这里有一个使用一堆 JVM 语言的简单例子,并使用了同样的命令行管道(这个例子使用 CLI 多行支持,通过 :{ 开始,:}结束 ):

>> :{
 frege sum [1,2,3] |
 groovy it.toInteger() + 2 |
 java line -> Integer.parseInt(line) + 3 |
 scala (line: String) => line.toInt + 4 | clj (fn [line] (+ (read-string line) 5)) |
 js function (line) { return line == 20; }
 :}
 true

编译指令可以写入任何 JVM 语言,还包括 Kotlin ,及 特别努力的 Ceylon 。你甚至不需要停止 CLI 重新加编译指令,就能看到更改的代码被重编译并重新加载。

只要你之前有足够长的 CLI 经验,你就会感到这个基于 JLine 的 CLI 不错,它支持命令历史,行编辑,通用 unix 快捷键(包括 vim/emacs 模块),管道(像上面演示的那样),tab 自动完成等等。

现在开始吧

如果你用 Gradle ,最容易的方式是运行下面脚本的 createOsgiRuntime 任务(运行 gradle 的crOsgi),然后使用生成的运行脚本。

详细步骤如下:

1. 保存下面的文件并命名为 build.gradle:

plugins {
    id "com.athaydes.osgi-run" version "1.5.4"
 }

 repositories {
    mavenLocal()
    jcenter()
 }

 dependencies {
    // the osgiaas-cli core bundle
    osgiRuntime 'com.athaydes.osgiaas:osgiaas-cli-core:0.7'

    // OSGi Service Component Runtime implementation
    osgiRuntime 'org.apache.felix:org.apache.felix.scr:2.0.2', {
        exclude group: '*'
    }
 }

 runOsgi {
    bundles = [ ] // all bundles added as osgiRuntime dependencies
 }

同样地, 运行下面的命令,下载并保存文件:

curl https://raw.githubusercontent.com/renatoathaydes/osgiaas/master/samples/osgiaas-cli-minimal.gradle --output build.gradle

2. 在同样的文件夹下,运行 createOsgiRuntime Gradle 任务(可以简单使用 crOsgi):

gradle crOsgi

OSGiaaS 使用 osgi-run  Gradle 插件来创建运行任务. 通过插件取得项目依赖, 从 JCenter 下载 并 全部放入一个 OSGi 运行任务。 OSGi  配置很简单 . 查看 osgi-run 文档获得更多信息.

3,使用下面命令开始 OSGiaas-CLI:

在 Linux/Mac 中:

bash build/osgi/run.sh

在 Windows 中:

build/osgi/run

你可以看看 OSGiaas CLI 的 ASCII 艺术 logo:

OSGiaaS-CLI — 可以用来运行 JVM 系语言 REPL 和命令的 CLI

如上所示。

你现在就可以准备使用 CLI。

点击 tab 几乎可以在任何地方实现自动补全,例如,想得到些帮助(在句末按空格键),然后 tab。

第一个你需要试的命令是 ps(Felix Shell 提供):

OSGiaaS-CLI — 可以用来运行 JVM 系语言 REPL 和命令的 CLI

这个命令展示当前系统中所有安装的库。

OSGi 默认的运行时 Apache Felix(就是上面提到的系统)。

如果你想使用不用的运行服务像 Equinox,改变 build.gradle 文件,添加 configSettings = 'equinox' 在 runOsgi 代码块内部用做 osgi-run 文档的解释。

Gradle 建立的文件是我们目前 OSGiaaS-CLI 运行,声明唯一最小的依赖设置。你可以点击 Tab 按键去看哪一个命令是有效的:

OSGiaaS-CLI — 可以用来运行 JVM 系语言 REPL 和命令的 CLI

OSGiaaS-CLI 文档给了详细解释对于他提供的命令用法,可是你可以使用帮助命令去看任意命令的使用信息:

OSGiaaS-CLI — 可以用来运行 JVM 系语言 REPL 和命令的 CLI

ci(内部详情命令)命令甚至给了一个命令的命令信息,包括那个 bundle 提供的命令和实现的类名(例如,ci -v alias).

其他的一些你从开始就应该知道的命令是:

shutdown - 退出 CLI

lr - 列出 JMV 资源

grep - 过滤文本

highlight - 高亮文本

run - 运行本机命令

使用命令时,你不需要一直输入,使用 CLI 当做 REPL 非常好用,像我们看到的那样。

因为 OSGiaaS-CLI 可以运行本机命令,你可以让它和 OSGiaaS-CLI 混合使用,就像你在自己的机子上使用 JVM 编写命令一样。

例如,运行 netstat(本地命令)和高亮所有包含 72 的代码行:(使用用 Java 编写的 CLI 的高亮命令)

在需要临时运行本地 shell 命令的时候,可以使用 use 命令(一旦通过 use 运行某个命令,所有用户输入都)会被当作它的参数:

OSGiaaS-CLI — 可以用来运行 JVM 系语言 REPL 和命令的 CLI

在使用某个命令的过程中,你可以在调用前缀 `_` 的命令。这会停止使用该命令。调用 _use,不带任何 W 参数,表示 “不 use 任何命令”。

从 CLI 转换到语言的 REPL,只需要 use 语言这样的命令:

OSGiaaS-CLI — 可以用来运行 JVM 系语言 REPL 和命令的 CLI

不过, 在使用语言命令之前,你得确保安装了这个语言! 下一节中就会看到怎样在环境中安装更多命令。

在继续这前,先运行关闭命令退出 CLI。

在 OSGi 运行时中,你可能需要输入 stop 0 来停止系统捆绑。

我比较喜欢定义一个别名:

>> alias exit="stop 0"

现在可以直接输入 exit 来退出。

为了保存这个别名,需要把上面的命令添加到初始化文件中,这个文件在 "${user.home}/.osgiaas_cli_init"。每次启动 CLI 都会运行这个文件,所以你可以用它定制 CLI。

我的整个初始化文件如下:

~/.osgiaas_cli_init:

color prompt blue
prompt "osgiaas> "
color error yellow
alias exit="stop 0"
alias hl=highlight

使用 Gradle 管理环境

想要管理安装到 OSGiaaS-CLI 环境中的库文件,使用上面开始用到的 Gradle 文件是最简单的。

举个例子,为了增加 JavaSlang 这样的 Java 库文件到 OSGiaaS-CLI 环境,只需要通过 Gradle 文件增加这个库文件的依赖 (增加的行加粗显示):

plugins {
   id "com.athaydes.osgi-run" version "1.5.2"
}

repositories {
   mavenLocal()
   jcenter()
}

dependencies {
   // the osgiaas-cli core bundle
   osgiRuntime 'com.athaydes.osgiaas:osgiaas-cli-core:0.7'
   
   osgiRuntime 'io.javaslang:javaslang:2.1.0-alpha'
   
   // OSGi Service Component Runtime implementation
   osgiRuntime 'org.apache.felix:org.apache.felix.scr:2.0.2', {
       exclude group: '*'
   }
}

runOsgi {
   bundles = [ ] // all bundles added as osgiRuntime dependencies

保存 Gradle 文件后, 确保重新创建环境:

gradle crOsgi

如果你想确保只安装 Gradle 文件中的库文件,还可以运行 clean 任务:  gradle clean crOsgi。

现在,重启 CLI,你会发现环境中已包含 JavaSlang 库文件。

可以用同样的方法添加其它有用的命令:

Java 命令:

osgiRuntime 'com.athaydes.osgiaas:osgiaas-cli-javac:0.7'

Groovy 命令:

osgiRuntime 'com.athaydes.osgiaas:osgiaas-cli-groovy:0.7'

Js 命令:

osgiRuntime 'com.athaydes.osgiaas:osgiaas-cli-js:0.7'

你已经学会了!

环境由 CLI 自身来管理。

很多命令只在 OSGi 环境中存在,比如 install(从某个 URL 安装包到系统中)、start(启动一个包),stop(停止启动的包)。

这些命令会让你感受到 OSGi 环境的魅力。如果你有 jar 包想安装在系统中,你可以使用 install 命令:

>> install file:///Users/renato/jars/my-lib.jar

install 命令使用 URL,所以你也可以从远程服务器获取 jar 文件。

install 命令不会自动启动安装好的包。start 命令也可以使用 URL 作为参数,它会先安装这个包再启动它。 多数情况下只需要使用 start 命令。

不过以这种方式来下载 jar 文件相当不方便,尤其是存在依赖链的时候。使用像 Gradle 或 Ivy 之类的包管理器要容易得多。

如果你想使用 Gradle 来获取 jar 文件,你可以使用上一节中提到的方法(在构建文件中添加 osgiRuntime 依赖,再次构建的时候它会被安装到系统中)。

OSGiaaS-CLI 还提供了一个办法,直接在 CLI 中使用 Apache Ivy

你通过在 Gradle 文件中添加下面的依赖项,在 CLI 中安装 ivy 命令:

Ivy 命令:

osgiRuntime 'com.athaydes.osgiaas:osgiaas-cli-ivy:0.7'

只要你愿意,可以用当前包含 Ivy 命名的的构建文件代码“default” CLI 构建,除了调试配置:

curl https://raw.githubusercontent.com/renatoathaydes/osgiaas/master/samples/osgiaas-cli-default.gradle --output build.gradle

之后,用 gradle clean crOsgi 重新构建项目,再启动 CLI。

现在你可以使用 ivy 命令检索存在于本地 Maven 库或 JCenter(包含 Maven核心工具) 的工具。比如,要检索 JavaSlang 并立即启动,输入如下命令:

>> ivy io.javaslang:javaslang | start

你可以使用一个 CLI 语言模块 轻松地使用 JavaSlang(或其它你想使用的库),比如用 Grovvy:

>> use groovy
Using 'groovy'. Type _use to stop using it.
>> import javaslang.collection.List
>> List.of(1,2,3).intersperse("-").mkString()
1-2-3

通过 osgiaas-cli-groovy 文档的指南来了解如何运行 Groovy 模块。不幸的是,大多数语言模块都需要一些小小的配置,因为它们中大多数使用的不是由 OSGi 环境导出的非标准类...比如 Grovvy 模块需要 run.reflect。你也可以从一个  示例 的 Gradle 文件开始。

写被编译的命令

创建一个新的,基础的 CLI 命令,你需要去创建一个类实现 org.apache.felix.shell.Command 接口。

这有一个 Java Hello World 命令,例如:

package com.athaydes.osgiaas.examples.java;

import com.athaydes.osgiaas.cli.CommandHelper;
import org.apache.felix.shell.Command;
import org.osgi.service.component.annotations.Component;

import java.io.PrintStream;
import java.util.List;

@Component( immediate = true, name = "hello-java" )
public class HelloJavaCommand implements Command {

   @Override
   public String getName() {
       return "hello-java";
   }

   @Override
   public String getUsage() {
       return "hello-java [<message>]";
   }

   @Override
   public String getShortDescription() {
       return "Prints a Hello World message or a custom message given by the user";
   }

   /**
    * This method implements the command logic.
    *
    * @param line full command provided by the user. Notice that this may be more than one line!
    * @param out  stream for the command output (prefer this to System.out)
    * @param err  stream for the command errors (prefer this to System.err)
    */
   @Override
   public void execute( String line, PrintStream out, PrintStream err ) {
       // break up the command line into separate tokens.
       // notice that the first part is always the name of the command itself.
       List<String> arguments = CommandHelper.breakupArguments( line );

       switch ( arguments.size() ) {
           case 1:// no arguments provided by the user
               out.println( "Hello Java!" );
               break;
           case 2:// The user gave an argument, print the argument instead
               out.println( "Hello " + arguments.get( 1 ) );
               break;
           default: // too many arguments provided by the user
               CommandHelper.printError( err, getUsage(), "Too many arguments" );
       }
   }

}

完整的命令同样能给用户提供些操作,如自动补齐和优化文档。OSCiaas 项目提供些设备能够简化这些操作。

ArgSpec 类可以用来说明可能使用的命令,包括能在标准方法中自动生成命令文档的文档。

为了展示他的工作原理,我们将创建一个 Weather 的命令,让用户看到当前全球的天气,就像下周的天气预报一样。

查看原文: OSGiaaS-CLI — 可以用来运行 JVM 系语言 REPL 和命令的 CLI

需要 登录 后回复方可回复, 如果你还没有账号你可以 注册 一个帐号。