【问题标题】:Groovy scripts no longer work under Cygwin?Groovy 脚本不再在 Cygwin 下工作?
【发布时间】:2025-08-11 20:35:01
【问题描述】:

在旧版本的 Groovy 中,我可以在 Cygwin 下将 Groovy 作为 shell 脚本运行,遵循他们自己的 instructions 这样做:

$ cat ~/bin/hiworld
#!/usr/bin/env groovy
println("Hello world")

这行得通。但是,在(至少)Groovy 2.3.2 和 2.3.3 下,我看到的是:

$ hiworld
Caught: java.net.MalformedURLException: unknown protocol: c
java.net.MalformedURLException: unknown protocol: c

我在黑暗中的最佳尝试:“env”通过绝对路径启动脚本(例如“groovy /home/myacct/bin/hiworld”),并且新版本的 Groovy 已经被“改进”了,因此 Groovy不再明白如何处理这个问题。

确实,我这样做也会产生同样的错误:

$ groovy ~/bin/hiworld
Caught: java.net.MalformedURLException: unknown protocol: c
java.net.MalformedURLException: unknown protocol: c

所以我不确定 Groovy 是如何 (a) 将其解析为 windows 样式的路径,然后 (b) 无法理解它是 windows 样式的路径。

然后,我可以通过运行它来“修复”它:

$ groovy $(cygpath -w ~/bin/hiworld)
Hello world

...但是,来吧,这是让用户启动实用程序脚本的一种完全疯狂的方式。 (当然,或者我可以只用那一行编写一个“前端”脚本来启动另一个脚本。但是,对于我最终想要完成的事情,我还不如放弃 Groovy 并分发一个可运行的带有相关启动脚本的 JAR。)

Groovy 是否只是放弃了对 Cygwin 的支持?或者他们真的有可能至少发布了两个版本而没有测试他们自己推荐的在最流行的环境之一下运行脚本的方法?

如果没有,我错过了什么或做错了什么?


更新:我认为备份我在这里建议的一些内容会很有帮助。

首先,我想说明 Cygwin 显然是(或曾经)至少在一定程度上受支持:例如,groovyStart 中有相当多的代码支持 Cygwin 平台(看起来和 Mac OSX 一样多)。如前所述,它显然在以前的版本中运行良好。

目前,最后一个示例在 groovyStart 下解析为:

'/cygdrive/c/Program Files/Java/jdk1.7.0_51/bin/java' -classpath C:/cygwin64/home/myacct/opt/groovy-2.3.3/lib/groovy-2.3.3.jar -Dscript.name=/home/myacct/opt/groovy-2.3.3/bin/groovy -Dprogram.name=groovy -Dgroovy.starter.conf=C:/cygwin64/home/myacct/opt/groovy-2.3.3/conf/groovy-starter.conf -Dgroovy.home=C:/cygwin64/home/myacct/opt/groovy-2.3.3 '-Dtools.jar=C:/Program Files/Java/jdk1.7.0_51/lib/tools.jar' org.codehaus.groovy.tools.GroovyStarter --main groovy.ui.GroovyMain --conf C:/cygwin64/home/myacct/opt/groovy-2.3.3/conf/groovy-starter.conf --classpath . C:/cygwin64/home/myacct/bin/hiworld

澄清一下,调用 JDK 本身没有问题——运行命令的那部分运行得很好。被破坏的部分是最后一个论点:如果我改变

C:/cygwin64/home/myacct/bin/hiworld

file:///C:/cygwin64/home/myacct/bin/hiworld

...它再次工作。这与我上面的断言一致,即“groovy”(脚本)确实正确地从 Cygwin/UNIX 样式的路径转换为本地 Windows 路径,但是在 Windows Java 中运行的底层进程实际上被混淆了窗户路径!显然它期待一个 URL。


更新 2: 下面,Warren 提出了尝试使用 GVM 的极好建议。可悲的是,这仍然会产生相同的错误:

$ which groovy
/home/myacct/.gvm/groovy/current/bin/groovy

$ hiworld
Caught: java.net.MalformedURLException: unknown protocol: c
java.net.MalformedURLException: unknown protocol: c

$ sh $(which groovy) ~/bin/hiworld
Caught: java.net.MalformedURLException: unknown protocol: c
java.net.MalformedURLException: unknown protocol: c

在前面的命令中添加“-x”标志表明 Groovy 仍在使用我的 Windows JVM(这并没有错,只是注意一下)并且现在正在引用 GVM 安装的库(为了便于阅读,此处稍微重新格式化) :

'/cygdrive/c/Program Files/Java/jdk1.7.0_51/bin/java' \
  -classpath C:/cygwin64/home/myacct/.gvm/groovy/2.3.3/lib/groovy-2.3.3.jar \
  -Dscript.name=/home/C400334/.gvm/groovy/current/bin/groovy \
  -Dprogram.name=groovy \
  -Dgroovy.starter.conf=C:/cygwin64/home/myacct/.gvm/groovy/2.3.3/conf/groovy-starter.conf \
  -Dgroovy.home=C:/cygwin64/home/myacct/.gvm/groovy/2.3.3 \
  '-Dtools.jar=C:/Program Files/Java/jdk1.7.0_51/lib/tools.jar' \
  org.codehaus.groovy.tools.GroovyStarter \
  --main groovy.ui.GroovyMain \
  --conf C:/cygwin64/home/myacct/.gvm/groovy/2.3.3/conf/groovy-starter.conf \
  --classpath . \
  C:/cygwin64/home/myacct/bin/hiworld

和以前一样,在最后一个参数之前添加“file:///”似乎可以解决问题。

所以我想知道我们是否使用了不同版本的 JVM 或其他什么?


更新 3: 升级到 jdk1.7.0_60(尝试了 64 位和 32 位版本),但这似乎没有什么不同。 Java 6 出现了同样的问题,但也增加了关于缺少 NioGroovyMethods 的抱怨。

【问题讨论】:

    标签: groovy cygwin


    【解决方案1】:

    我不确定 Groovy 是如何 (a) 将其解析为 Windows 样式的路径

    startGroovy 脚本中包含用于检测 Cygwin 并在必要时使用 cygpath 的特定代码。

    我是通过installing Groovy via GVM 发现的,这是获得适用于 Unixy 平台的 Groovy 的认可方式。 (在这种情况下,您不想使用 Groovy 的原生 Windows 发行版!)

    唯一棘手的事情是弄清楚如何设置JAVA_HOME。在这里,它必须是:

     $ export JAVA_HOME='/cygdrive/c/Program Files (x86)/Java/jre7'
    

    如果您使用的是 64 位版本的 Windows 并且像我一样安装了 32 位 JRE,那么这也应该适合您。否则,您可能需要调整路径。

    一旦我让gvm install groovy 成功,您的hiworld 示例在这里运行良好如果我以./hiworld 运行它。这是带有 Java 1.7.0_55 的 32 位版本的 Cygwin。

    但是,当我将它放入PATH 并以hiworld 运行时,正如您所做的那样,这传递了一个完全限定的路径到groovy 包装脚本(例如/home/wyoung/bin/hiworld)而不是相对路径,这导致startGroovy 脚本通过cygpath -m 运行路径,从而将其变成类似C:/cygwin64/home/wyoung/bin/hiworld 的东西。 Oracle 的 JRE 无法处理本地路径中的正斜杠。它盲目地假设 Windows 上的正斜杠意味着它是某种 URL,因此 C: 被视为 URL 方案,或者他们所说的“协议”。

    我不知道这是 Java 还是 startGroovy 脚本中的回归。尽管您可以在此处传递 file:// URL,但您也可以传递带有反斜杠而不是正斜杠的“正确”Windows 路径。你可以从cygpath 使用-w 开关而不是-m 获得。在这种情况下,您必须更加小心避免意外的反斜杠转义,这可以解释回归。

    【讨论】:

    • 首先,感谢您抽出宝贵时间撰写如此周到(且内容丰富)的回复!而且,虽然我(遗憾地)不同意您回答中的一些重要前提,但您已经将问题转移了一点。最重要的是:Cygwin 显然是 Groovy 的目标平台。澄清一下,我没有“设法”让他们推荐的脚本技术在 Cygwin 下工作,它得到了正确的支持,开箱即用,在 Groovy 2.1.1 下没有问题——非常好。我怀疑您的解决方案可以工作,但它对我的用户来说太多了,更不用说我了。更多内容。
    • 有趣的是它对你有用!我升级的全部原因是因为一位同事尝试了该脚本,并遇到了我在此处记录的问题。所以我下载了三个新版本的 Groovy(通过 ZIP 和 Windows 安装),并确定即使是简单的“hello world”显然也不再工作了。所以有趣的是,我的两个独立测试都显示了这一点,而你的则没有。我想知道有什么不同!?无论如何,谢谢你调查它,沃伦。
    • 我应该澄清一下:我说我想知道有什么不同,我的意思是“我想知道 GVM 正在做什么有什么不同?”可悲的是,有问题的行为似乎仍然几乎相同。 (记录在上面。)我想知道我们是否可能使用不同版本的 JVM?无论如何,再次感谢您提供有用的想法和建议!
    • 试过 1.7.0_60,32 位和 64 位版本。没有运气。
    【解决方案2】:

    刚刚遇到同样的问题,将 startGroovy 脚本中的 exec 调用更改为以下内容(注意最后两行):

        exec "$JAVACMD" $JAVA_OPTS \
            -classpath "$STARTER_CLASSPATH" \
            -Dscript.name="$SCRIPT_PATH" \
            -Dprogram.name="$PROGNAME" \
            -Dgroovy.starter.conf="$GROOVY_CONF" \
            -Dgroovy.home="$GROOVY_HOME" \
            -Dtools.jar="$TOOLS_JAR" \
            $STARTER_MAIN_CLASS \
            --main $CLASS \
            --conf "$GROOVY_CONF" \
            --classpath "$CP" \
            "$(cygpath -w $1)" \
            "${@:2}"
    

    ... 这样,被调用的脚本会被更改为带有反斜杠的 windows 路径,如上所述,然后不会被解释为似乎是 URL。讨厌的 hack,但至少我能够再次执行我的脚本 :)

    【讨论】:

    • 我有类似的工作,效果很好。但是,如果尝试执行“groovy -h”或只是运行“groovy”,那么 cygpath -w 会出错。
    【解决方案3】:

    不确定是否还有人对此感兴趣。但是不久我评论了上面的答案,我找到了解决方案。

    使用以下两个更改更新“startGroovy”脚本:

    for dir in $ROOTDIRSRAW ; do
        ROOTDIRS="$ROOTDIRS$SEP$dir"
        SEP="|"
    done
    ROOTDIRS="/c$SEP$ROOTDIRS"  ## <<<< ADD THIS LINE;assuming cygwin on c: drv
    

    并将 --mixed 替换为 --windows:

    CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
    if [ $CHECK -ne 0 ] ; then
        ### change the --mixed to --windows here
        patched=`cygpath --path --ignore --windows "$arg"`  
    else
        patched="$arg"
    fi
    

    它应该可以工作。

    【讨论】: