代码之家  ›  专栏  ›  技术社区  ›  Kaleb Pederson

在爪哇中将包含命令行参数的字符串分割为字符串[]

  •  26
  • Kaleb Pederson  · 技术社区  · 16 年前

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

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

    鉴于上述情况,Java通常会将以下内容传递给MaGe:

    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在这些条件下那样解析字符串吗?

    注释 我做 不是 需要进行命令行分析,我已经在使用 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 ,但它只支持 String 数组。同样, GetOpt 构造函数还需要 String[] --因此需要一个解析器。

    7 回复  |  直到 9 年前
        1
  •  24
  •   Mr_and_Mrs_D    12 年前

    下面是一个非常简单的替代方法,可以将文件中的文本行拆分为参数向量,以便将其输入到选项分析器中:

    这就是解决方案:

    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放到类路径上,要么只接受命令行类,因为所使用的方法是静态的。

        2
  •  9
  •   user177800    16 年前

    你应该使用全功能的 现代的 面向对象的命令行参数解析器我推荐我最喜欢的 Java Simple Argument Parser . 和 how to use JSAP 这是使用Groovy作为一个例子,但是对于直的Java来说是一样的。也有 args4j 它在某些方面比JSAP更现代,因为它使用注释,远离ApACH.Cuff.CLI的东西,它是旧的和被破坏的,并且非常程序化,并且UnJava在其API中是等价的。但我仍然依赖于JSAP,因为它很容易构建自己的自定义参数处理程序。

    对于URL、数字、inetaddress、颜色、日期、文件、类,有很多默认的解析器,并且非常容易添加您自己的解析器。

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

    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有一种非常好的方法来声明代码之外的选项和设置,因此您的代码中没有成百上千行的设置,这些设置会使真正的功能代码变得混乱和模糊,请参见我的链接 如何使用JSAP 例如,代码比我尝试过的其他库少。

    这是您的问题的一个方向解决方案,如更新中所阐明的那样。 ,您的“script”文件中的行仍然是命令行。逐行从文件中读取并调用 JSAP.parse(String); .

    我一直使用这种技术为Web应用程序提供“命令行”功能。一个特别的用途是在一个大型的多人在线游戏中使用Director/Flash前端,我们可以像聊天一样执行“命令”,并在后端使用JSAP来解析它们,并根据解析的内容执行代码。非常像您想要做的,除了您从一个文件而不是从一个套接字读取“命令”。我放弃joptsimple,只使用jsap,它强大的可扩展性真的会让您扫兴。

        3
  •  8
  •   nvamelichev    11 年前

    如果只需要支持类似Unix的操作系统,那么还有更好的解决方案 . 不像 Commandline 从蚂蚁, ArgumentTokenizer 来自drjava的更多 sh -比如:它支持逃跑!

    说真的,哪怕是什么 精神错乱的 喜欢 sh -c 'echo "\"un'\''kno\"wn\$\$\$'\'' with \$\"\$\$. \"zzz\""' 正确标记为 [bash, -c, echo "\"un'kno\"wn\$\$\$' with \$\"\$\$. \"zzz\""] (顺便说一下,当运行时,这个命令输出 "un'kno"wn$$$' with $"$$. "zzz" )

        4
  •  3
  •   qiangbro    9 年前
    /**
     * [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()]);
    }
    
        5
  •  1
  •   Community Mohan Dere    9 年前

    扩展在 Andreas_D's answer ,而不是复制,使用 CommandLineUtils.translateCommandline(String toProcess) 从优秀的 Plexus Common Utilities 图书馆。

        6
  •  -1
  •   InsertNickHere    16 年前

    我想这个可以帮助你:

    CLI

        7
  •  -1
  •   Andy    16 年前

    我用 Java Getopt port 做这件事。