带有Weka命令行的ProcessBuilder / Runtime.exec()展示了独特的行为

时间:2018-08-09 13:33:27

标签: java command-line runtime weka processbuilder

以下基本上是我所遇到的全部问题的MCVE,这更加混乱。您需要知道的是,直接插入终端时,以下行会运行:

java -classpath /path/to/weka.jar weka.filters.MultiFilter \
    -F "weka.filters.unsupervised.attribute.ClusterMembership -I first" \
    -i /path/to/in.arff

这是相对简单的。基本上,我所做的就是尝试使用in.arff过滤器的所有默认设置来对ClusterMembership的数据进行聚类,但是我想忽略第一个属性。我在那里有MultiFilter,因为在我的实际项目中还有其他过滤器,因此我需要保留它。就像前面提到的,这很好。但是,当我尝试使用ProcessBuilder运行同一行时,出现“报价解析错误”,而且嵌套报价的整个结构似乎都崩溃了。证明这一点的一种方法是尝试使以下内容起作用:

List<String> args = new ArrayList<String>();
args.add("java");
args.add("-cp"); 
args.add("/path/to/weka.jar");
args.add("weka.filters.MultiFilter");
args.add("-F");
args.add("\"weka.filters.unsupervised.attribute.ClusterMembership"); 
args.add("-I"); 
args.add("first\"");
args.add("-i"); 
args.add("/path/to/in.arff");
ProcessBuilder pb = new ProcessBuiler(args);

// ... Run the process below

乍一看,您可能会认为这与上面的代码是相同的(这肯定是我幼稚的自己的想法)。实际上,如果我只打印args,并且每个字符之间都留有空格,那么直接复制并粘贴到终端后,结果字符串是相同的,并且运行良好。但是,由于任何原因,该程序将无法正常运行,因为我收到了来自Weka的消息Quote parse error。我尝试使用Google搜索,发现关于this的问题,有关ProcessBuilder如何在命令行中添加额外的引号(这使我尝试了许多转义序列组合,所有这些均无效),并阅读了this文章,内容涉及ProcessBuilder / Runtime.exec()的工作方式(我尝试使用ProcessBuilder和Runtime.exec(),最终仍然存在相同的问题),但是找不到与我所需的任何内容。 Weka已经有糟糕的文档,然后由于Wikispaces关闭而在几周前关闭了他们的Wikispace页面,所以我发现Weka方面的信息很少。

然后我的问题是:是否有办法像上面运行的第二个示例那样运行,以便可以将参数组合在一起以处理更大的命令?我了解它可能需要一些时髦的转义序列(或可能不需要?),或者可能是我未考虑的其他内容。非常感谢您的任何帮助。

编辑:我更新了问题,希望可以对我的问题有更多的了解。

1 个答案:

答案 0 :(得分:1)

您不需要将参数分组在一起。正如您已经提到的,它甚至不起作用。看看当我这样调用Java程序时会发生什么:

java -jar Test.jar -i -s "-t 500"

这是我的“程序”:

public class Test {
  public static void main(String[] args) {
    for( String arg : args ) {
      System.out.println(arg);
    }      
  }
}

这是输出:

-i
-s
-t 500

引号不包含在参数中,不包含,用于对参数进行分组。因此,当您像以前一样将参数传递给ProcessBuilder时,本质上就像是在命令行上用引号将它们写成一样,它们被视为单个参数,这会使解析器感到困惑。

仅当您具有嵌套组件时,才需要引号。 FilteredClassifier。也许my answer on another Weka question可以帮助您处理这些嵌套的组件。 (我最近更改了指向他们的Wiki的链接,使其指向Google缓存,直到他们建立了一个新的Wiki。)

由于您没有指定确切的大小写导致您考虑分组,因此您可以尝试获取适用于Weka的命令行,然后将其用作我的程序的输入。然后,您可以查看将它们传递给ProcessBuilder的方式。

以您的示例为例,我猜下面的方法会起作用:

List<String> args = new ArrayList<String>();
args.add("java");
args.add("-cp"); 
args.add("/path/to/weka.jar");
args.add("weka.filters.MultiFilter");
args.add("-F");
args.add("weka.filters.unsupervised.attribute.ClusterMembership -I first");
args.add("-i"); 
args.add("/path/to/in.arff");
ProcessBuilder pb = new ProcessBuiler(args);

其他详细信息

在Weka内部发生的事情基本上是以下内容:参数中的选项首先由weka.filters.Filter处理,然后所有非通用过滤器选项由weka.filters.MultiFilter处理,{ {1}}:

setOptions(...)

此处,filters = new Vector<Filter>(); while ((tmpStr = Utils.getOption("F", options)).length() != 0) { options2 = Utils.splitOptions(tmpStr); filter = options2[0]; options2[0] = ""; filters.add((Filter) Utils.forName(Filter.class, filter, options2)); } tmpStr选项的值,将由-Fsource code)处理。在那里,所有引用和取消引用的操作都发生了,因此下一个组件将接收一个options数组,该数组看起来像是一级组件一样。