我有一个 groovy 脚本,它需要一个 jar 中的库。如何将其添加到类路径中?我希望脚本是可执行的,所以我在#!/usr/bin/env groovy
脚本的顶部使用。
9 回答
启动一个 groovy 脚本#!/usr/bin/env groovy
有一个非常重要的限制 -不能添加额外的参数。不能配置类路径,不能运行带有定义或调试的 groovy。这不是一个常规问题,而是 shebang ( #!
) 工作方式的限制 - 所有附加参数都被视为单个参数,因此#!/usr/bin/env groovy -d
告诉/usr/bin/env
运行命令groovy -d
然后groovy
使用d
.
该问题有一个解决方法,它涉及在 groovy 脚本中使用 bash 引导 groovy。
#!/bin/bash
//usr/bin/env groovy -cp extra.jar:spring.jar:etc.jar -d -Dlog4j.configuration=file:/etc/myapp/log4j.xml "$0" $@; exit $?
import org.springframework.class.from.jar
//other groovy code
println 'Hello'
所有的魔法都发生在前两行。第一行告诉我们这是一个bash
脚本。bash
开始运行并看到第一行。Inbash
#
用于评论,//
折叠到/
哪个是根目录。So bash
will run/usr/bin/env groovy -cp extra.jar:spring.jar:etc.jar -d -Dlog4j.configuration=file:/etc/myapp/log4j.xml "$0" $@
它以我们想要的所有参数开始 groovy。是我们脚本的"$0"
路径,$@
也是参数。现在 groovy 运行,它忽略前两行并看到我们的 groovy 脚本,然后退出回到bash
. bash
然后退出 ( exit $?1
) 并带有来自 groovy 的状态码。
如果你真的需要,你也可以在运行时加载一个 JAR:
this.getClass().classLoader.rootLoader.addURL(new File("file.jar").toURL())
您可以将罐子添加到 $HOME/.groovy/lib
我最喜欢的方法是使用 Groovy Grapes。这些访问 Maven 中央存储库,下载引用的 jar,然后将其放在类路径中。然后,您可以像使用任何其他库一样使用该库。语法非常简单:
@Grab(group='com.google.collections', module='google-collections', version='1.0')
您可以在此处阅读更多详细信息。这里的一个主要优点是在分发脚本时不需要分发依赖项。此方法的唯一缺点是 Jar 必须位于 Maven 存储库中。
你也可以试试 Groovy Grape。它允许您使用注释来修改类路径。它现在是实验性的,但非常酷。请参阅docs.groovy-lang.org/.../grape
就像在 Java 中一样。
这是运行 MySQL 状态监视脚本的示例。mysql.jar 包含我从脚本 status.groovy 调用的 MySQL 连接器。
groovy -cp mysql.jar status.groovy ct1
下面是Patrick 的解决方案、Maarteen Boekhold 的解决方案和 foozbar 的评论的组合,适用于 Linux 和 Cygwin:
#!/bin/bash
// 2>/dev/null; SCRIPT_DIR="$( cd "$( dirname "$0" )" && pwd )"
// 2>/dev/null; OPTS="-cp $SCRIPT_DIR/lib/extra.jar:$SCRIPT_DIR/lib/spring.jar"
// 2>/dev/null; OPTS="$OPTS -d"
// 2>/dev/null; OPTS="$OPTS -Dlog4j.configuration=file:/etc/myapp/log4j.xml"
// 2>/dev/null; exec groovy $OPTS "$0" "$@"; exit $?
import org.springframework.class.from.jar
//other groovy code
println 'Hello'
这个怎么运作:
//
是一个有效的 groovy 注释,因此所有 bash 命令都被 Groovy 忽略。//
将返回错误,但错误输出被重定向到/dev/null
,因此不显示。- 即使前一个命令失败,bash 也会执行分号后面的命令。
exec
替换当前进程中的当前程序而不派生新进程。因此,groovy 在原始脚本进程中运行(ps
将进程显示为脚本而不是 groovy 可执行文件)- 下面的
exit $?
语句exec groovy
阻止 bash 尝试将脚本的其余部分解释为 bash 脚本,并且还保留了 groovy 脚本的返回代码。
在某些情况下,上述 bash 技巧比RootLoader 技巧更方便,因为您可以在脚本中使用常规导入语句。使用 RootLoader 技巧会强制您使用反射加载所有类。这在某些情况下很好(例如当您需要加载 JDBC 驱动程序时),但在其他情况下不方便。
如果您知道您的脚本永远不会在 Cygwin 上执行,那么使用 Patrick 或 Maarteen 的解决方案可能会导致性能稍好一些,因为它们避免了生成和丢弃错误的开销。
添加到@Patrick 他的回答对我帮助很大,我最近发现了另一个技巧。
如果您在一行中将大量 jars 添加到类路径中,事情可能变得非常难以理解。但是您可以执行以下操作!
#!/bin/bash
//bin/true && OPTS="-cp blah.jar -Dmyopt=value"
//bin/true && OPTS="$OPTS -Dmoreopts=value2"
//usr/bin/env groovy $OPTS "$0" $@; exit $?
println "inside my groovy script"
让您的想象力疯狂地想象您可以通过这种方式将命令行分解成可管理的部分有多复杂
马丁
如果您想在声明之前import
立即使用它,可以这样:):
// printEmployees.groovy
this.class.classLoader.rootLoader.addURL(
new URL("file:///C:/app/Dustin/product/11.1.0/db_1/jdbc/lib/ojdbc6.jar"))
import groovy.sql.Sql
sql = Sql.newInstance("jdbc:oracle:thin:@localhost:1521:orcl", "hr", "hr",
"oracle.jdbc.pool.OracleDataSource")
sql.eachRow("SELECT employee_id, last_name, first_name FROM employees")
{
println "The employee's name is ${it.first_name} ${it.last_name}."
}
取自这篇javaworld.com 文章。