如何在Windows中设置长的Java类路径?

53

我正在尝试在Windows XP命令行中手动运行一个特定的JUnit测试,该测试类路径中有异常高数量的元素。我尝试了几种不同的变化方式,例如:

set CLASS_PATH=C:\path\a\b\c;C:\path\e\f\g;....
set CLASS_PATH=%CLASS_PATH%;C:\path2\a\b\c;C:\path2\e\f\g;....
...
C:\apps\jdk1.6.0_07\bin\java.exe -client oracle.jdevimpl.junit.runner.TestRunner com.myco.myClass.MyTest testMethod

(其他变化是将classpath全部设置在一行上,通过-classpath作为java参数来设置classpath)。最终控制台总是会抛出以下错误:
The input line is too long.
The syntax of the command is incorrect.

这是一个JUnit测试,用于测试一个相当大的现有遗留项目,因此不建议重新排列目录结构以获得更合理的方案,这些类型的解决方案暂时不可行。 我只是试图快速针对此项目生成一个测试,并在命令行上运行它,但控制台拒绝响应。 希望得到帮助!

13个回答

61

在这方面,Windows命令行非常受限。一种解决方法是创建一个“路径jar”文件。它是一个仅包含Manifest.mf文件的jar,它的Class-Path指定了您长列表中jar等文件的磁盘路径。现在只需将此路径jar添加到您的命令行类路径中。这通常比打包实际资源更加方便。

据我记得,磁盘路径可以相对于路径jar本身。因此,Manifest.mf可能看起来像这样:

Class-Path: this.jar that.jar ../lib/other.jar

如果你的 路径jar 主要包含基础资源,则它不会经常更改,但是您可能仍然希望在构建过程中某处生成它。例如:

<jar destfile="pathing.jar">
  <manifest>
    <attribute name="Class-Path" value="this.jar that.jar ../lib/other.jar"/>
  </manifest>
</jar>

2
自从Ant 1.7版本以来,可使用ManifestClassPath任务来从Ant路径生成适用于Class-Path属性的合适属性。 - Matt
1
我需要做什么,例如确保这个jar已加载吗?还是JVM将扫描类路径上提供的所有jar以查找此类清单?另外,它在JVM之间是否可移植?支持此功能的最低Java版本是多少? - bacar
1
我尝试过这个,但Manifest.mf仅允许使用相对URL链接到其他JAR文件和目录。 - Dr. Max Völkel
类路径列表每行的最大字符数为72。那么在Manifest.MF文件中Class-Path的限制是什么? - Varun Jain
请注意,这是Maven Surefire的默认行为,但最近的Java更新使其更加困难。具体信息请参见:https://maven.apache.org/surefire/maven-surefire-plugin/examples/class-loading.html和https://issues.apache.org/jira/browse/SUREFIRE-1588。 - Jesse Glick

21
自从Java 6以来,您可以使用 类路径通配符
例如:foo/* 表示目录foo中的所有 .jar 文件。
注意事项:
  • 这不会匹配类文件(仅限jar文件)。要匹配两者,使用:foo;foo/*foo/*;foo。顺序决定了首先加载什么。
  • 搜索不是递归的。

1
这在Java 6中似乎是新的。 - Chris Noe
1
嘿,太酷了 - 我不知道那个。那绝对是一个有用的选项,特别是如果(像我们一样)类路径充满了许多在同一个目录下的jar文件。 - Ogre Psalm33
但是这并没有提供递归遍历。 - Snehal Masne
这似乎是一个“仅链接答案”。您应该提供更多关于如何进行的细节。 - rghome

20

在Java 9及以上版本上使用“参数文件”

在Java 9及以上版本中,java可执行文件支持通过文件提供参数。请参见https://docs.oracle.com/javase/9/tools/java.htm#JSWOR-GUID-4856361B-8BFD-4964-AE84-121F5F6CF111

该机制明确旨在解决操作系统对命令长度的限制问题:

您可以使用@argument文件缩短或简化java命令,以指定包含参数(例如选项和类名称)的文本文件,这些参数将传递给java命令。这使得您可以在任何操作系统上创建任意长度的java命令。

在命令行上,使用@符号前缀标识包含java选项和类名称的参数文件。当java命令遇到以@符号(@)开头的文件时,它会将该文件的内容扩展为参数列表,就像它们在命令行上指定的那样。

如果您运行的是9或更高版本,则这是“正确”的解决方案。此机制仅修改了向JVM提供参数的方式,因此与任何框架或应用程序完全兼容,无论它们如何加载类,即它与通常在命令行上提供参数完全等效。对于基于清单的绕过此操作系统限制的解决方法,情况并非如此。

以下是其示例:

原始命令:

java -cp c:\foo\bar.jar;c:\foo\baz.jar

可以重写为:

java @c:\path\to\cparg

其中c:\path\to\cparg是一个包含以下内容的文件:

-cp c:\foo\bar.jar;c:\foo\baz.jar

这个“参数文件”还支持使用行继续字符和引号来正确处理路径中的空格,例如:

-cp "\
c:\foo\bar.jar;\
c:\foo\baz.jar"

Gradle

如果你在使用Gradle时遇到了这个问题,请参考这个插件,它会自动将你的classpath转换成一个"参数文件"并在Windows执行exec或test任务时提供给JVM。在Linux或其他操作系统上,默认情况下不会执行任何操作,但是可以使用可选的配置值来应用转换而不考虑操作系统。

https://github.com/redocksoft/classpath-to-file-gradle-plugin

(免责声明:我是作者)

另请参阅相关的Gradle问题--希望这种功能最终能够集成到Gradle核心中:https://github.com/gradle/gradle/issues/1989


4

(我想你并不是真的指DOS,而是指cmd.exe。)

我认为这不是一个CLASSPATH的限制,而是环境大小/环境变量大小的限制。在XP上,单个环境变量的大小可以达到8k,整个环境的大小限制为64k。我看不出你会达到那个限制。

在Windows上有一个限制,限制了命令行的长度,在WindowsNT+上它是8k,适用于cmd.exe。set命令也受到该限制。你的set命令中可能有超过8k的目录吗?如果是这样,即使像Nick Berardi建议的那样拆分它们,你也可能没有办法。


哎呀,是的,老派的东西正在渗透。是的,cmd.exe。 - Ogre Psalm33

2
感谢Raman为Java 9+的路径问题引入了新的解决方案。我对bootRun任务进行了一些修改,允许使用Gradle已经评估过的所有内容来运行带有参数文件的Java。虽然不是很优雅,但是可行。"Original Answer"翻译成"最初的回答"。
// Fix long path problem on Windows by utilizing java Command-Line Argument Files 
// https://docs.oracle.com/javase/9/tools/java.htm#JSWOR-GUID-4856361B-8BFD-4964-AE84-121F5F6CF111 
// The task creates the command-line argument file with classpath
// Then we specify the args parameter with path to command-line argument file and main class
// Then we clear classpath and main parameters
// As arguments are applied after applying classpath and main class last step 
// is done to cheat gradle plugin: we will skip classpath and main and manually
// apply them through args
// Hopefully at some point gradle will do this automatically 
// https://github.com/gradle/gradle/issues/1989 

if (Os.isFamily(Os.FAMILY_WINDOWS)) {
    bootRun {
        doFirst {
            def argumentFilePath = "build/javaArguments.txt"
            def argumentFile = project.file(argumentFilePath)
            def writer = argumentFile.newPrintWriter()
            writer.print('-cp ')
            writer.println(classpath.join(';'))
            writer.close()

            args = ["@${argumentFile.absolutePath}", main]
            classpath = project.files()
            main = ''
        }
    }
}


请问您能解释一下在哪里添加这些代码行吗? - Ahmad
这段代码是用于Spring Boot应用程序的build.gradle文件。 - user1921819

1
如果我是你,我会从微软下载连接实用程序:http://technet.microsoft.com/en-us/sysinternals/bb896768.aspx,然后将你的"C:\path"映射到"z:\",将"c:\path2"映射到"y:\"。这样,你就可以减少classpath中每个项目4个字符。
set CLASS_PATH=C:\path\a\b\c;C:\path\e\f\g;
set CLASS_PATH=%CLASS_PATH%;C:\path2\a\b\c;C:\path2\e\f\g;

现在,你的类路径将会是:

set CLASS_PATH=z\a\b\c;z\e\f\g;
set CLASS_PATH=%CLASS_PATH%;y:\a\b\c;y:\e\f\g;

根据您实际的classpath,它可能会执行更多操作。


Junction 是 NTFS 中的一个前端,mklink /D 是另一个,可能已经存在于较新版本的 Windows 中。 - mgaert
mlink似乎没有包含在Windows 7中。junction作为Windows 7(企业版)的一部分包含在内。 - anjanb

0
你可以试试这个。
@echo off
set A=D:\jdk1.6.0_23\bin
set B=C:\Documents and Settings\674205\Desktop\JavaProj
set PATH="%PATH%;%A%;"
set CLASSPATH="%CLASSPATH%;%B%;"

打开命令提示符并运行两次(不知道为什么...我必须在Windows XP机器上这样做) 路径仅设置为当前命令提示符会话


0

除了将jar文件移动到类似于“C:\ jars”的文件夹中以缩短类路径之外,没有其他解决此问题的方法。


3
不正确。还有另外一种解决方案,但你可能不知道它是什么,或者你决定不使用它。然而,你所做的肯定是一种解决方案......在某些情况下可以发挥作用。 - Stephen C

0

我认为你在这里陷入了泥潭。

命令行对调用程序的参数有限制。

我有两个建议供您尝试。

第一,在运行junit测试之前,您可以让脚本/ant_task创建类路径上各种类的JAR文件。然后您可以将JAR文件放在类路径上,这样应该就会更短。

另一个尝试的方法是创建一个antscript来运行JUNIT,在ANTS中不应该有类路径条目的限制。


0
正如HuibertGill所提到的,我建议将其封装在一个Ant构建脚本中,这样您就不必自己管理所有内容了。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接