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

Kal*_*son 27 java parsing command-line-arguments

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

-p /path -d "here's my description" --verbose other args
Run Code Online (Sandbox Code Playgroud)

鉴于上述情况,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
Run Code Online (Sandbox Code Playgroud)

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

:我不是需要做的命令行解析,我已经使用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
Run Code Online (Sandbox Code Playgroud)

然后用户将运行我的管理工具,如下所示:

adminTool --script /path/to/above/file
Run Code Online (Sandbox Code Playgroud)

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

joptsimple附带了一个具有解析方法的Parser ,但它只支持一个String数组.类似地,GetOpt构造函数也需要String[]- 因此需要解析器.

And*_*s_D 25

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

这是解决方案:

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);
}
Run Code Online (Sandbox Code Playgroud)

魔法类Commandline蚂蚁的一部分.所以你要么必须把ant放在类路径上,要么只需要使用Commandline类,因为使用的方法是静态的.

  • 还是唯一的方法吗?核心库中有什么东西吗? (4认同)
  • 实施(337行):[translateCommandline](https://commons.apache.org/proper/commons-exec/apidocs/src-html/org/apache/commons/exec/CommandLine.html) (2认同)

小智 8

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

URL,数字,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");
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

而且我不喜欢通过XML进行配置编程,但JSAP有一种非常好的方法可以在代码之外声明选项和设置,因此您的代码不会被数百行设置混乱,这些设置会混淆和模糊真实的功能代码,请参阅我的链接,了解如何使用JSAP作为示例,比我尝试的任何其他库更少的代码.

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

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


小智 8

如果您只需要支持类UNIX操作系统,那么有一个更好的解决方案.与Commandlineant 不同,DrJava的ArgumentTokenizer更像是sh:它支持转义!

说真的,甚至疯狂的东西sh -c 'echo "\"un'\''kno\"wn\$\$\$'\'' with \$\"\$\$. \"zzz\""'都被正确地标记为[bash, -c, echo "\"un'kno\"wn\$\$\$' with \$\"\$\$. \"zzz\""](顺便说一下,当运行时,这个命令输出"un'kno"wn$$$' with $"$$. "zzz").


qia*_*bro 5

/**
 * [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()]);
}
Run Code Online (Sandbox Code Playgroud)