在另一个常规中包含常规脚本


97

我已经阅读了如何在另一个groovy脚本中简单地导入groovy文件

我想在一个groovy文件中定义通用功能,并从其他groovy文件中调用这些功能。

我知道这将像脚本语言一样使用Groovy,即,我不需要类/对象。我正在尝试可以在groovy中完成的类似dsl的操作。所有变量都将从Java声明,我想在shell中执行groovy脚本。

这有可能吗?有人可以提供一些例子。


Answers:


107
evaluate(new File("../tools/Tools.groovy"))

将其放在脚本的顶部。这将引入groovy文件的内容(只需用groovy脚本替换双引号之间的文件名)。

我使用一个令人惊讶的名为“ Tools.groovy”的类来执行此操作。


7
文件名需要符合Java的类命名规则才能起作用。
willkil 2013年

2
问题-如何使用此语法将参数传递给正在评估的脚本?
史蒂夫

3
@steve不能,但是您可以在该脚本中定义一个带有参数调用的函数
Nilzor 2014年

11
它不起作用...脚本得到了很好的评估,但调用方范围(def,class等)中没有声明
LoganMzz

3
您必须从调用1返回对象,然后将评估结果分配给变量。
LoganMzz

45

从Groovy 2.2开始,可以使用新的@BaseScriptAST转换批注声明基本脚本类。

例:

文件MainScript.groovy

abstract class MainScript extends Script {
    def meaningOfLife = 42
}

文件test.groovy

import groovy.transform.BaseScript
@BaseScript MainScript mainScript

println "$meaningOfLife" //works as expected

1
使用此方法时,我不断收到“无法解析类”的信息。你会推荐我做什么?有什么方法可以将自定义类导入到另一个groovy脚本中?
droidnoob

38

执行此操作的另一种方法是在groovy类中定义函数并在运行时解析并将文件添加到类路径:

File sourceFile = new File("path_to_file.groovy");
Class groovyClass = new GroovyClassLoader(getClass().getClassLoader()).parseClass(sourceFile);
GroovyObject myObject = (GroovyObject) groovyClass.newInstance();

2
该解决方案实际上对我来说效果最好。当我尝试使用接受的答案时,出现一条错误消息,提示我的主要groovy脚本无法解析评估脚本中定义的类。为了它的价值……
cBlaine 2014年

1
我尝试了在SO上发布的几种不同方法,但只有这种方法有效。其他人则引发了有关无法解析类或方法的错误。这是我使用的版本Groovy版本:2.2.2 JVM:1.8.0供应商:Oracle Corporation操作系统:Windows 7
Kuberchaun 2014年

这很棒。确保GroovyObject明确使用,这不是您自己的类名的占位符。
haventcheck

1
我仍然得到:java.lang.NoClassDefFoundError:groovy.lang.GroovyObject
dokaspar

救生员。谢谢你,小伙伴!!
Anjana Silva

30

我认为最好的选择是以常规类的形式组织实用程序,将它们添加到类路径中,然后让主脚本通过import关键字引用它们。

例:

脚本/DbUtils.groovy

class DbUtils{
    def save(something){...}
}

脚本/script1.groovy:

import DbUtils
def dbUtils = new DbUtils()
def something = 'foobar'
dbUtils.save(something)

运行脚本:

cd scripts
groovy -cp . script1.groovy

我想知道如果您拥有与libsrc目录类似的目录结构,它将如何工作
Gi0rgi0s

9

我这样做的方法是GroovyShell

GroovyShell shell = new GroovyShell()
def Util = shell.parse(new File('Util.groovy'))
def data = Util.fetchData()

6

Groovy没有像典型脚本语言那样的import关键字,它会在字面上包含另一个文件的内容(在此不行:groovy是否提供include机制?)。
由于它具有面向对象/类的本质,因此您必须“玩游戏”才能使这种工作正常进行。一种可能是使所有实用程序函数均为静态(因为您说它们不使用对象),然后在执行的Shell上下文中执行静态导入。然后,您可以将这些方法称为“全局函数”。
另一种可能性是使用Binding对象(http://groovy.codehaus.org/api/groovy/lang/Binding.html),同时创建您的Shell并将所需的所有功能绑定到方法(此处的缺点是必须枚举绑定中的所有方法,但您也许可以使用反射)。另一个解决方案是重写methodMissing(...)分配给您的shell的委托对象,这使您基本上可以使用映射或所需的任何方法进行动态调度。

这里展示了其中的几种方法:http : //www.nextinstruction.com/blog/2012/01/08/creating-dsls-with-groovy/。如果您想查看特定技术的示例,请告诉我。


7
此链接现在已失效
Nicolas Mommaerts 2014年


5

这是将一个脚本包含在另一个脚本中的完整示例。
只需运行Testmain.groovy文件,
其中包含解释性注释,因为我很喜欢;]

Testutils.groovy

// This is the 'include file'
// Testmain.groovy will load it as an implicit class
// Each method in here will become a method on the implicit class

def myUtilityMethod(String msg) {
    println "myUtilityMethod running with: ${msg}"
}

测试维护

// Run this file

// evaluate implicitly creates a class based on the filename specified
evaluate(new File("./Testutils.groovy"))
// Safer to use 'def' here as Groovy seems fussy about whether the filename (and therefore implicit class name) has a capital first letter
def tu = new Testutils()
tu.myUtilityMethod("hello world")

0

对于后来者来说,groovy现在支持:load file-path简单地从给定文件重定向输入的命令,因此现在包含库脚本变得微不足道了。

它可以作为groovysh的输入和已加载文件中的一行:
groovy:000> :load file1.groovy

file1.groovy可以包含:
:load path/to/another/file invoke_fn_from_file();


您可以对此进行扩展吗?文档在哪里?我放在哪里:load file-path
ChristofferHammarström'17

好吧,它可以作为groovysh的输入和已加载文件中的一行:<br/> groovy:000> :load file1.groovy file1.groovy可以包含:<br/>:load path/to/another/file
Jack Punt

1
在docs中找到了负载。如果我理解正确,它适用于groovysh吗?
ChristofferHammarström'17

但是,这不适用于变量中定义的路径吗?
user2173353 '19

0

@grahamparks和@snowindy答案的结合进行了一些修改,这对我在Tomcat上运行的Groovy脚本有效:

实用程序

class Utils {
    def doSth() {...}
}

MyScript.groovy:

/* import Utils --> This import does not work. The class is not even defined at this time */
Class groovyClass = new GroovyClassLoader(getClass().getClassLoader()).parseClass(new File("full_path_to/Utils.groovy")); // Otherwise it assumes current dir is $CATALINA_HOME
def foo = groovyClass.newInstance(); // 'def' solves compile time errors!!
foo.doSth(); // Actually works!

我得到:java.lang.NoClassDefFoundError:groovy.lang.GroovyObject
dokaspar

0

Groovy可以完全像Java一样导入其他Groovy类。只要确保库文件的扩展名是.groovy。

    $ cat lib/Lib.groovy
    package lib
    class Lib {
       static saySomething() { println 'something' }
       def sum(a,b) { a+b }
    }

    $ cat app.gvy
    import lib.Lib
    Lib.saySomething();
    println new Lib().sum(37,5)

    $ groovy app
    something
    42

-1

经过一番调查,我得出以下结论:以下方法似乎是最好的。

一些/子包/ Util.groovy

@GrabResolver(name = 'nexus', root = 'https://local-nexus-server:8443/repository/maven-public', m2Compatible = true)
@Grab('com.google.errorprone:error_prone_annotations:2.1.3')
@Grab('com.google.guava:guava:23.0')
@GrabExclude('com.google.errorprone:error_prone_annotations')

import com.google.common.base.Strings

class Util {
    void msg(int a, String b, Map c) {
        println 'Message printed by msg method inside Util.groovy'
        println "Print 5 asterisks using the Guava dependency ${Strings.repeat("*", 5)}"
        println "Arguments are a=$a, b=$b, c=$c"
    }
}

example.groovy

#!/usr/bin/env groovy
Class clazz = new GroovyClassLoader().parseClass("${new File(getClass().protectionDomain.codeSource.location.path).parent}/some/subpackage/Util.groovy" as File)
GroovyObject u = clazz.newInstance()
u.msg(1, 'b', [a: 'b', c: 'd'])

为了运行example.groovy脚本,请将其添加到系统路径并从任何目录键入:

example.groovy

脚本打印:

Message printed by msg method inside Util.groovy
Print 5 asterisks using the Guava dependency *****
Arguments are a=1, b=b, c=[a:b, c:d]

上面的示例在以下环境中进行了测试: Groovy Version: 2.4.13 JVM: 1.8.0_151 Vendor: Oracle Corporation OS: Linux

该示例演示了以下内容:

  • 如何Util在Groovy脚本中使用类。
  • 通过UtilGuava第三方库包含为Grape依赖项(@Grab('com.google.guava:guava:23.0'))来调用第三方库的类。
  • Util班可以驻留在一个子目录。
  • 将参数传递给Util类中的方法。

其他意见/建议:

  • 始终使用groovy类而不是groovy脚本来实现groovy脚本中的可重用功能。上面的示例使用在Util.groovy文件中定义的Util类。将groovy脚本用于可重用功能是有问题的。例如,如果使用groovy脚本,则必须在脚本的底部使用实例化Util类new Util(),但最重要的是,必须将其放置在名为Util.groovy的文件中。有关groovy脚本和groovy类之间的区别的更多详细信息,请参阅脚本与类。
  • 在上述示例中,我使用路径"${new File(getClass().protectionDomain.codeSource.location.path).parent}/some/subpackage/Util.groovy"而不是"some/subpackage/Util.groovy"。这样可以确保Util.groovy始终根据groovy脚本的位置(example.groovy)而不是当前工作目录找到该文件。例如,使用"some/subpackage/Util.groovy"会导致在搜索WORK_DIR/some/subpackage/Util.groovy
  • 请遵循Java类命名约定来命名groovy脚本。我个人更喜欢一个小的偏差,即脚本以小写字母开头,而不是大写字母。例如,myScript.groovy是脚本名称,并且MyClass.groovy是类名称。my-script.groovy在某些情况下,命名将导致运行时错误,因为生成的类将没有有效的Java类名。
  • 通常,在JVM世界中,相关功能称为JSR 223:Java脚本。特别是在Groovy中,该功能被称为Groovy集成机制。实际上,可以使用相同的方法从Groovy或Java中调用任何JVM语言。此类JVM语言的一些著名示例是Groovy,Java,Scala,JRuby和JavaScript(Rhino)。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.