2010-07-15 108 views
23

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

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

鉴于上述情况,爪哇通常会通过以下的主:

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会在这些情况下进行解析?

注意:我做需要做的命令行解析,我已经使用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[] - 因此需要解析器。

+3

难道你不能只使用main()给你的args数组,而不是试图自己解析它? – 2010-07-15 19:24:36

+0

我已经更新了我的问题来描述为什么我需要解析字符串以及它与命令行解析的区别。 – 2010-07-15 19:42:39

+0

我不认为它与命令行解析有什么不同,请参阅我的回答的附录,以了解我过去如何处理与此类似的内容。 – 2010-07-15 19:55:20

回答

23

这里是分割从一个文件中的文本行成一个参数向量,这样你可以喂到你选择的解析器一个非常简单的选择:

这是解决方案:

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

的魔术班Commandlineant的一部分。所以你要么必须把ant放在类路径上,要么就把Commandline类作为静态方法使用。

+0

作为文档,'translateCommandline'处理包括单引号和双引号字符串并在其中转义,但不能像POSIX shell那样识别反斜杠,因为在基于DOS的系统上会导致问题。 – 2010-07-15 22:46:30

+0

有一个蚂蚁的来源分布。在这一点上,我会执行'translateCommandline'并对其进行修改以适应我的需要。 – 2010-07-16 06:07:33

+0

真的很棒的解决方案。太感谢了。井井有条。 – 2012-01-30 10:11:43

9

您应该使用全功能的现代面向对象的命令行参数解析器我建议我最喜欢的Java Simple Argument Parser。和how to use JSAP,这是使用Groovy作为例子,但它是一样的直Java。还有args4j,它在某些方面比JSAP更现代,因为它使用注释,远离apache.commons.cli的东西,它在API中是旧的和破坏性的,非常程序化和非Java的。但是我仍然依靠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配置编程的粉丝,但司法制度评估方案有一个非常好的方式来宣告你的代码之外的选项和设置,所以你的代码不会被数百行设置混淆在一起,这些设置会混淆并遮蔽真正的功能代码,请参阅我在how to use JSAP上的链接,比我试过的任何其他库更少的代码。

这是一个方向解决您的问题,因为在你的更新澄清,在你的“脚本”文件中的行仍然命令行。从文件中逐行阅读它们,并致电JSAP.parse(String);

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

+0

JSAP是我看到接受一个字符串的第一个解析器,但不幸的是,它返回一个'JSAPResult'而不是'String []',所以我将无法使用它而不切换我的命令行解析库:(。 – 2010-07-15 19:45:01

+0

'String []'是非常没用的,JSAP结果的全部原因是它全部解析和规则执行,并检查你,我认为,如果你真的退后一步,重新思考你的方法,并且一些重构将会非常有益,请参阅我的更新,根据你最后一次编辑。 – 2010-07-15 19:54:09

+0

我不想构建一个shell字符串解析器,'line.split(“”)'不够智能。它会死在创建'Array [3]'的参数上,正如我在文章中指出的那样,因为参数可能同时包含空格和转义序列。我需要一个完整的解析器来处理所有的可能性 - 但我需要一个String []解析器的字符串,而不是一个命令行解析器。 – 2010-07-15 20:04:02

-1

我想这人会帮助你:

CLI

+0

这是旧的并且被破坏并且一个可怕的建议,至少有3个更新的支持和非支持越野车全功能的替代品,不要使用这个。 – 2010-07-15 19:26:53

+3

@fuzzy很高兴知道。但降低投票并不是必要的,因为这个答案是正确的。 ;-) – InsertNickHere 2010-07-15 19:28:29

+0

不好的建议比没有建议更糟:-( – 2010-07-15 19:30:00

-1

我用Java Getopt port做到这一点。

+1

除非我错过了某些东西,getopt端口不会取一个字符串,只有一个'String []'。 – 2010-07-15 19:45:53

+0

你可以详细说明如何使用它吗?只是一个链接不太好。 – 2017-10-10 17:01:36

8

如果您需要仅支持类似UNIX的操作系统,那么还有更好的解决方案。不像来自蚂蚁的Commandline,来自DrJava的ArgumentTokenizer更像是sh -like:它支持逃脱!

严重的是,甚至一些疯狂sh -c 'echo "\"un'\''kno\"wn\$\$\$'\'' with \$\"\$\$. \"zzz\""'被正确标记化到[bash, -c, echo "\"un'kno\"wn\$\$\$' with \$\"\$\$. \"zzz\""](顺便说一下,在运行时,此命令的输出"un'kno"wn$$$' with $"$$. "zzz")。

1
/** 
* [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()]); 
} 
相关问题