将包含命令行参数的字符串拆分为Java中的String []

时间:2010-07-15 19:19:15

标签: java parsing command-line-arguments

this thread for C#类似,我需要将包含命令行参数的字符串拆分到我的程序中,这样我就可以让用户轻松运行多个命令。例如,我可能有以下字符串:

-p /path -d "here's my description" --verbose other args

鉴于上述情况,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

我不需要担心任何shell扩展,但它必须足够智能来处理单引号和双引号以及字符串中可能存在的任何转义。有没有人知道在这些条件下shell会解析字符串的方法吗?

注意:我 NOT 需要进行命令行解析,我已经使用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

然后用户将按如下方式运行我的管理工具:

adminTool --script /path/to/above/file
然后

main()将找到--script选项并迭代文件中的不同行,将每行拆分为一个数组,然后我将在joptsimple实例中将其反射,然后传入我的应用程序驱动程序。

joptsimple带有一个parse method的Parser,但它只支持String数组。同样,GetOpt构造函数也需要String[] - 因此需要解析器。

7 个答案:

答案 0 :(得分:24)

这是将文本行从文件拆分为参数向量的一种非常简单的替代方法,以便您可以将其提供给选项解析器:

这是解决方案:

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);
}

魔术课Commandline ant 的一部分。因此,您必须将ant放在类路径上,或者只是使用Commandline类,因为使用的方法是静态的。

答案 1 :(得分:9)

你应该使用一个功能齐全的现代面向对象的命令行参数解析器我建议我最喜欢的Java Simple Argument Parser。而how to use JSAP,这是以Groovy为例,但直接Java也是如此。还有args4j在某些方面比JSAP更现代,因为它使用注释,远离apache.commons.cli的东西,它是旧的和破坏的,它的API非常程序化和非Java-eques 。但我仍然依赖于JSAP,因为构建自己的自定义参数处理程序非常容易。

网址,数字,InetAddress,颜色,日期,文件,类等都有很多默认的解析器,而且添加自己的解析器非常容易。

例如,这里有一个将args映射到枚举的处理程序:

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");
        }
    }
}

并且我不喜欢通过XML进行配置编程,但JSAP有一种非常好的方式来声明代码之外的选项和设置,因此您的代码不会被数百行设置混乱,这些设置会混淆和模糊真实功能代码,请参阅我在how to use JSAP上的链接获取示例,代码少于我尝试过的任何其他库。

这是您的更新中阐明的问题的方向解决方案 ,“脚本”文件中的行仍然是命令行。逐行从文件中读取它们并调用JSAP.parse(String);

我使用此技术始终为Web应用程序提供“命令行”功能。一个特别的用途是在一个带有Director / Flash前端的大型多人在线游戏中,我们启用了来自聊天的“命令”,并在后端使用JSAP来解析它们并根据它解析的内容执行代码。非常类似于您想要做的事情,除了您从文件而不是套接字读取“命令”。我会抛弃joptsimple而只是使用JSAP,你会被它强大的可扩展性所破坏。

答案 2 :(得分:8)

如果您只需要支持类UNIX操作系统,那么就有更好的解决方案。与来自ant的Commandline不同,来自DrJava的ArgumentTokenizer更多sh - 就像:它支持转义!

说真的,即使像<{1}}这样的疯狂也会被正确地标记为sh -c 'echo "\"un'\''kno\"wn\$\$\$'\'' with \$\"\$\$. \"zzz\""'(顺便说一句,运行时,此命令会输出[bash, -c, echo "\"un'kno\"wn\$\$\$' with \$\"\$\$. \"zzz\""])。

答案 3 :(得分:3)

/**
 * [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()]);
}

答案 4 :(得分:1)

展开Andreas_D's answer,而不是复制,使用优秀的Plexus Common Utilities库中的CommandLineUtils.translateCommandline(String toProcess)

答案 5 :(得分:-1)

我猜这个会帮到你:

CLI

答案 6 :(得分:-1)

我使用Java Getopt port来做。