将包含命令行参数的字符串拆分为 Java 中的 String[]
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3259143/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Split a string containing command-line parameters into a String[] in Java
提问by Kaleb Pederson
Similar to this thread for C#, I need to split a string containing the command line arguments to my program so I can allow users to easily run multiple commands. For example, I might have the following string:
类似于C# 的这个线程,我需要将包含命令行参数的字符串拆分到我的程序中,以便我可以让用户轻松运行多个命令。例如,我可能有以下字符串:
-p /path -d "here's my description" --verbose other args
Given the above, Java would normally pass the following in to main:
鉴于上述情况,Java 通常会将以下内容传递给 main:
Array[0] = -p
Array[1] = /path
Array[2] = -d
Array[3] = here's my description
Array[4] = --verbose
Array[5] = other
Array[6] = args
I don't need to worry about any shell expansion, but it must be smart enough to handle single and double quotes and any escapes that may be present within the string. Does anybody know of a way to parse the string as the shell would under these conditions?
我不需要担心任何 shell 扩展,但它必须足够智能以处理单引号和双引号以及字符串中可能存在的任何转义。有人知道在这些条件下像 shell 那样解析字符串的方法吗?
NOTE: I do NOTneed to do command line parsing, I'm already using joptsimpleto do that. Rather, I want to make my program easily scriptable. For example, I want the user to be able to place within a single file a set of commands that each of which would be valid on the command line. For example, they might type the following into a file:
注:我不是需要做的命令行解析,我已经使用joptsimple做到这一点。相反,我想让我的程序易于编写脚本。例如,我希望用户能够在单个文件中放置一组命令,每个命令在命令行上都是有效的。例如,他们可能会在文件中键入以下内容:
--addUser admin --password Admin --roles administrator,editor,reviewer,auditor
--addUser editor --password Editor --roles editor
--addUser reviewer --password Reviewer --roles reviewer
--addUser auditor --password Auditor --roles auditor
Then the user would run my admin tool as follows:
然后用户将按如下方式运行我的管理工具:
adminTool --script /path/to/above/file
main()will then find the --scriptoption and iterate over the different lines in the file, splitting each line into an array that I would then fire back at a joptsimple instance which would then be passed into my application driver.
main()然后将找到该--script选项并遍历文件中的不同行,将每一行拆分为一个数组,然后我将在一个 joptsimple 实例上回火,然后将其传递到我的应用程序驱动程序中。
joptsimple comes with a Parser that has a parse method, but it only supports a Stringarray. Similarly, the GetOptconstructors also require a String[]-- hence the need for a parser.
joptsimple 带有一个 Parser ,它有一个parse 方法,但它只支持一个String数组。同样,GetOpt构造函数也需要一个String[]-- 因此需要一个解析器。
回答by Andreas Dolk
Here is a pretty easy alternative for splitting a text line from a file into an argument vector so that you can feed it into your options parser:
这是一个非常简单的替代方法,用于将文件中的文本行拆分为参数向量,以便您可以将其输入到选项解析器中:
This is the solution:
这是解决方案:
public static void main(String[] args) {
String myArgs[] = Commandline.translateCommandline("-a hello -b world -c \"Hello world\"");
for (String arg:myArgs)
System.out.println(arg);
}
The magic class Commandlineis part of ant. So you either have to put ant on the classpath or just take the Commandline class as the used method is static.
魔法类Commandline是ant 的一部分。因此,您要么必须将 ant 放在类路径上,要么只使用 Commandline 类,因为所使用的方法是静态的。
回答by nvamelichev
If you need to support only UNIX-like OSes, there is an even better solution. Unlike Commandlinefrom ant, ArgumentTokenizerfrom DrJava is more sh-like: it supports escapes!
如果您只需要支持类 UNIX 操作系统,还有更好的解决方案。与Commandlineant不同,DrJava 的ArgumentTokenizer更像sh:它支持转义!
Seriously, even something insanelike sh -c 'echo "\"un'\''kno\"wn\$\$\$'\'' with \$\"\$\$. \"zzz\""'gets properly tokenized into [bash, -c, echo "\"un'kno\"wn\$\$\$' with \$\"\$\$. \"zzz\""](By the way, when run, this command outputs "un'kno"wn$$$' with $"$$. "zzz").
严重的是,甚至一些疯狂喜欢sh -c 'echo "\"un'\''kno\"wn\$\$\$'\'' with \$\"\$\$. \"zzz\""'被正确标记化到[bash, -c, echo "\"un'kno\"wn\$\$\$' with \$\"\$\$. \"zzz\""](顺便说一下,在运行时,此命令的输出"un'kno"wn$$$' with $"$$. "zzz")。
回答by nvamelichev
You should use a fully featured modernobject oriented Command Line Argument Parser I suggest my favorite Java Simple Argument Parser. And how to use JSAP, this is using Groovy as an example, but it is the same for straight Java. There is also args4jwhich is in some ways more modern than JSAP because it uses annotations, stay away from the apache.commons.cli stuff, it is old and busted and very procedural and un-Java-eques in its API. But I still fall back on JSAP because it is so easy to build your own custom argument handlers.
你应该使用一个功能齐全的现代面向对象的命令行参数解析器我推荐我最喜欢的Java 简单参数解析器。以及如何使用JSAP,这里以Groovy为例,但对于纯Java也是一样的。还有args4j,它在某些方面比 JSAP 更现代,因为它使用注释,远离 apache.commons.cli 的东西,它是旧的和破坏的,并且在其 API 中非常程序化和非 Java 队列。但我仍然依赖 JSAP,因为构建您自己的自定义参数处理程序非常容易。
There are lots of default Parsers for URLs, Numbers, InetAddress, Color, Date, File, Class, and it is super easy to add your own.
有许多用于 URL、数字、InetAddress、颜色、日期、文件、类的默认解析器,添加自己的解析器非常容易。
For example here is a handler to map args to Enums:
例如,这是一个将 args 映射到 Enum 的处理程序:
import com.martiansoftware.jsap.ParseException;
import com.martiansoftware.jsap.PropertyStringParser;
/*
This is a StringParser implementation that maps a String to an Enum instance using Enum.valueOf()
*/
public class EnumStringParser extends PropertyStringParser
{
public Object parse(final String s) throws ParseException
{
try
{
final Class klass = Class.forName(super.getProperty("klass"));
return Enum.valueOf(klass, s.toUpperCase());
}
catch (ClassNotFoundException e)
{
throw new ParseException(super.getProperty("klass") + " could not be found on the classpath");
}
}
}
and I am not a fan of configuration programming via XML, but JSAP has a really nice way to declare options and settings outside your code, so your code isn't littered with hundreds of lines of setup that clutters and obscures the real functional code, see my link on how to use JSAPfor an example, less code than any of the other libraries I have tried.
而且我不喜欢通过 XML 进行配置编程,但是 JSAP 有一种非常好的方法可以在代码之外声明选项和设置,因此您的代码不会充斥着数百行设置,这些设置会使真正的功能代码变得混乱和模糊,请参阅我关于如何使用 JSAP 的链接作为示例,代码比我尝试过的任何其他库都要少。
This is a direction solution to your problem as clarified in your update, the lines in your "script" file are still command lines. Read them in from the file line by line and call JSAP.parse(String);.
这是您在更新中阐明的问题的方向解决方案,“脚本”文件中的行仍然是命令行。从文件中逐行读取它们并调用JSAP.parse(String);.
I use this technique to provide "command line" functionality to web apps all the time. One particular use was in a Massively Multiplayer Online Game with a Director/Flash front end that we enabled executing "commands" from the chat like and used JSAP on the back end to parse them and execute code based on what it parsed. Very much like what you are wanting to do, except you read the "commands" from a file instead of a socket. I would ditch joptsimple and just use JSAP, you will really get spoiled by its powerful extensibility.
我一直使用这种技术为网络应用程序提供“命令行”功能。一个特殊的用途是在带有 Director/Flash 前端的大型多人在线游戏中,我们允许从聊天中执行“命令”,并在后端使用 JSAP 来解析它们并根据解析的内容执行代码。非常像您想要做的,除了您从文件而不是套接字读取“命令”。我会放弃 joptsimple 而只使用 JSAP,你真的会被它强大的可扩展性所宠坏。
回答by qiangbro
/**
* [code borrowed from ant.jar]
* Crack a command line.
* @param toProcess the command line to process.
* @return the command line broken into strings.
* An empty or null toProcess parameter results in a zero sized array.
*/
public static String[] translateCommandline(String toProcess) {
if (toProcess == null || toProcess.length() == 0) {
//no command? no string
return new String[0];
}
// parse with a simple finite state machine
final int normal = 0;
final int inQuote = 1;
final int inDoubleQuote = 2;
int state = normal;
final StringTokenizer tok = new StringTokenizer(toProcess, "\"\' ", true);
final ArrayList<String> result = new ArrayList<String>();
final StringBuilder current = new StringBuilder();
boolean lastTokenHasBeenQuoted = false;
while (tok.hasMoreTokens()) {
String nextTok = tok.nextToken();
switch (state) {
case inQuote:
if ("\'".equals(nextTok)) {
lastTokenHasBeenQuoted = true;
state = normal;
} else {
current.append(nextTok);
}
break;
case inDoubleQuote:
if ("\"".equals(nextTok)) {
lastTokenHasBeenQuoted = true;
state = normal;
} else {
current.append(nextTok);
}
break;
default:
if ("\'".equals(nextTok)) {
state = inQuote;
} else if ("\"".equals(nextTok)) {
state = inDoubleQuote;
} else if (" ".equals(nextTok)) {
if (lastTokenHasBeenQuoted || current.length() != 0) {
result.add(current.toString());
current.setLength(0);
}
} else {
current.append(nextTok);
}
lastTokenHasBeenQuoted = false;
break;
}
}
if (lastTokenHasBeenQuoted || current.length() != 0) {
result.add(current.toString());
}
if (state == inQuote || state == inDoubleQuote) {
throw new RuntimeException("unbalanced quotes in " + toProcess);
}
return result.toArray(new String[result.size()]);
}
回答by Sagi
Expanding on Andreas_D's answer, instead of copying, use CommandLineUtils.translateCommandline(String toProcess)from the excellent Plexus Common Utilitieslibrary.
扩展Andreas_D 的答案,而不是复制,使用CommandLineUtils.translateCommandline(String toProcess)优秀的Plexus Common Utilities库。
回答by Andy
I use the Java Getopt portto do it.
我使用Java Getopt 端口来做到这一点。

