windows JVM 命令行参数上的尾随星号在 cygwin 中被通配 bash shell

trailing asterisks on windows JVM command-line args are globbed in cygwin bash shell

更新:当 运行在 cygwin bash shell 中使用基于 JVM 的命令行工具时会出现此问题。虽然我最初认为这与 Scala 有关,但它特定于 Windows JVM。这可能是 MSDN 库中重大更改的结果,请参阅下面的评论。

我正在编写一个 scala 实用程序脚本,它接受文字 java 类路径条目并对其进行分析。我希望我的主要方法能够接收带有尾随星号的命令行参数,例如“/*”,但是当 运行 在 cygwin [=62] 中时似乎没有办法做到这一点=] session.

这是我的 scala 测试脚本,它显示命令行参数:

# saved to a file called "dumpargs.sc"
args.foreach { printf("[%s]\n",_) }

我希望能够使用星号作为参数来调用它,如下所示:

scala -howtorun:script dumpargs.sc "*"

当我 运行 在 CMD.EXE shell 中执行此操作时,它符合我的预期:

c:\cygwin> scala.bat -howtorun:script dumpargs.sc "*"
arg[*]
c:\cygwin>

同样,当在 Linux bash shell 中测试时,唯一的命令行参数由一个星号组成,同样符合预期。

一个用 C 编写的可比较的命令行参数转储程序打印一个裸星号,无论 shell 它是来自(CMD.EXE 或 bash 的 运行 ).

但是当同一个测试在 cygwin bash shell 中是 运行 时,星号是全局的,列出了当前目录中的所有文件。 globbing 发生在 bash 下游的某处,否则 C 转储程序也会失败。

这个问题很微妙,它发生在JVM的某个地方,在它接收到星号参数之后,在JVM调用main方法之前。但是 JVM 只会根据 运行ning shell 环境中的某些东西来匹配星号。

在某些方面,这种行为是一件好事,因为它支持脚本可移植性,通过隐藏 运行 时间环境、Windows 与 Linux/OSX 等的差异( unix-like shells 倾向于 glob,而 CMD.EXE 则不会)。

到目前为止解决该问题的所有努力都失败了:

即使我允许使用 os 依赖的技巧,我也尝试了以下所有方法(来自 bash 会话):

"*" '*' '\*' '\*'

以下 almost 有效,但半引号作为参数值的一部分到达,然后必须被我的程序剥离:

"'*'"

同样的问题,但不同类型的不需要的引号通过了:

'"*"' or \"*\"

我们需要的是一个系统 属性,或其他一些禁用 globbing 的机制。

顺便说一句,这个问题的一个变体是无法利用将 jar 文件目录添加到类路径的好方法(自 java 1.6 起),方法是指定“-classpath” 'lib/*'".

需要一个系统 属性 当 运行 在 shell 提供自己的 globbing 的环境中时,我可以设置禁用此行为。

此问题是由 JVM 中的一个已知错误引起的,记录在此处:

https://bugs.openjdk.java.net/browse/JDK-8131329

同时,为了解决这个问题,我通过环境变量传递参数。

这是我的 "myScalaScript" 里面发生的事情:

#!/usr/bin/env scala
for( arg <- args.toList ::: cpArgs ){
  printf("[%s]\n",arg)
}

lazy val cpArgs = System.getenv("CP_ARGS") match {
  case null => Nil
  case text => text.split("[;|]+").toList
}

以下是从 bash 调用脚本的方式: CP_ARGS=".|./lib/*" myScalaScript [可能是其他非问题参数]

这是它在所有测试环境中打印的内容:

[.]
[./lib/*]

这里有一个更好的修复方法,它隐藏了脚本中的所有脏东西,并且在主循环中更加传统。

新脚本:

#!/bin/bash
export CP_ARGS="$@"
exec $(which scala) "[=12=]"
!#
// vim: ft=scala

for( arg <- cpArgs ){
  printf("[%s]\n",arg)
}

lazy val cpArgs = System.getenv("CP_ARGS") match {
  case null => Nil
  case text => text.split("[;|]+").toList
}