Windows上的尾随星号JVM命令行参数是cygwin bash shell中的globbed

问题描述:

UPDATE:当在cygwin bash shell中运行基于JVM的命令行工具时,会出现这个问题。虽然我最初认为这是与Scala相关,它是专门针对Windows JVM。这可能是MSDN库中的更改的结果,请参阅下面的注释。

UPDATE: this problem occurs when running JVM-based command line tools in a cygwin bash shell. Although I originally thought this was related to Scala, it's specific to the Windows JVM. It might be the result of breaking changes in MSDN libraries, see comments below.

我正在写一个scala实用程序脚本,它接受一个文本java类路径条目并分析它。我想我的主要方法能够接收带有星号的命令行参数,例如/ *,但是似乎没有办法在cygwin bash会话中运行。

I'm writing a scala utility script that takes a literal java classpath entry and analyzes it. I'd like my main method to be able to receive command line arguments with a trailing asterisk, e.g, "/*", but there seems to be no way to do it when running in a cygwin bash session.

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

Here's my scala test script, which displays command line arguments:

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

我想以星号作为参数来调用它,例如:

I'd like to be able to call it with an asterisk as an argument, like this:

scala -howtorun:script dumpargs.sc "*"

.EXE shell,它做我想要的:

When I run this in a CMD.EXE shell, it does what I expect:

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

同样,在Linux bash shell中测试时,唯一的命令行参数由一个单独的星号

Likewise, when tested in a Linux bash shell, the sole command line argument consists of a single bare asterisk, again as expected.

用C语言编写的类似的命令行参数转储程序会打印一个单独的星号,而不管它是从哪个shell运行的(CMD.EXE或bash)。

A comparable command-line args dumper program written in C prints a single bare asterisk, regardless of which shell it is run from (CMD.EXE or bash).

但是当在cygwin bash shell中运行相同的测试时,星号是globbed,列出当前目录中的所有文件。

But when the same test is run in a cygwin bash shell, the asterisk is globbed, listing all the files in the current directory. The globbing happens somewhere downstream from by bash, since otherwise, the C dumper program would have also failed.

问题是微妙的,它发生在JVM中的某个地方,在它收到之后星号参数和JVM调用main方法之前。但是JVM只在运行的shell环境中基于某个东西对星号进行全球化。

The problem is subtle, it happens somewhere in the JVM after it receives the asterisk argument and before the JVM calls the main method. But the JVM only globs the asterisk based on something in the running shell environment.

在某些方面,这种行为是一件好事,因为它支持脚本可移植性,隐藏运行时环境中的差异,Windows与Linux / OSX等(unix式shell往往是glob,而CMD.EXE不会)。

In some ways, this behaviour is a good thing, since it supports script-portability, by hiding differences in the runtime environments, Windows versus Linux/OSX, etc (unix-like shells tend to glob, whereas CMD.EXE doesn't).

工作周围的问题,到目前为止已经失败:

All efforts to work around the problem so far have failed:

即使我允许os相关的技巧,我已经尝试所有以下(从一个bash会话) :

Even if I'm allow for os-dependent tricks, I've tried all of the following (from a bash session):

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

以下几乎可以工作,但是半引号作为参数值的一部分到达,然后必须被我的程序删除: / p>

The following almost works, but the half-quotes arrive as part of the argument value and must then been stripped away by my program:

"'*'"

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

Same problem, but different kind of unwanted quotes get through:

'"*"' or \"*\"

需要的是系统属性或某种其他机制来禁用globbing。

What's needed is a system property, or some other mechanism to disable globbing.

顺便说一下,这个问题的一个变体是无法利用好的方式将jar文件的目录添加到类路径(从java 1.6开始) ,通过指定-classpath'lib /*'\".

By the way, one variation of this problem is the inability to take advantage of the nice way a directory of jar files can be added to the classpath (since java 1.6), by specifying "-classpath 'lib/*'".

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

There needs to be a system property I can set to disable this behavior when running in a shell environment that provide its' own globbing.

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

This problem is caused by a known bug in the JVM, documented here:

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

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

In the meantime, to get around the problem, I'm passing arguments via an environment variable.

这里是我的myScalaScript内发生的情况:

Here's what happens inside my "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 [可能是其他非问题的参数]

Here's how the script is invoked from bash: CP_ARGS=".|./lib/*" myScalaScript [possibly other-non-problematic-args]

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

and here's what it prints in all tested environments:

[.]
[./lib/*]

这里有一个更好的解决方法,隐藏脚本中的所有内疚,并且在主循环中更加常规。

Here's a better fix, that hides all the nastiness inside the script, and is a bit more conventional in the main loop.

新脚本:

#!/bin/bash
export CP_ARGS="$@"
exec $(which scala) "$0"
!#
// 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
}