从Java执行外部程序

7

我正在尝试从Java代码中执行一个程序。以下是我的代码:

public static void main(String argv[]) {
    try {
      String line;
      Process p = Runtime.getRuntime().exec(
          "/bin/bash -c ls > OutputFileNames.txt");
      BufferedReader input = new BufferedReader(
          new InputStreamReader(p.getInputStream()));
      while ((line = input.readLine()) != null) {
        System.out.println(line);
      }
      input.close();
    } catch (Exception err) {
      err.printStackTrace();
    }
}

我的操作系统是Mac OS X 10.6。

如果我从getRuntime().exec()方法中删除"> OutputFileNames.txt",所有文件名都可以正常打印到控制台。但我需要将它打印到一个文件中。

另外,如果我将命令更改为:

Process p = Runtime.getRuntime().exec(
    "cmd \c dir > OutputFileNames.txt"); 

如果在Windows上运行,它可以正常运行并将结果完美地打印到文件中。

我已经阅读了其他关于从Java执行另一个应用程序的帖子,但似乎都与我的问题无关。

非常感谢您能提供的任何帮助。

谢谢,


如果您只想列出目录中的文件,那么没有必要通过外部程序来实现 - 您可以直接使用File.listFiles()并自己将结果写入文件。这在任何操作系统上都能正常工作,而且不需要特定于Mac OS X或Windows的代码。 - Jesper
这只是我尝试的基本操作。最终我想要做的是运行一个外部程序,并将该程序的输出打印到文件中,然后从Java中解析该文件以执行其他有用的操作。 - Saurabh Lalwani
1
你尝试过使用 .exec(new String[]{"/bin/bash", "-c", "ls > OutputFileNames.txt"}); 吗?它也失败了吗? - Anton
谢谢你的快速回复 Anton。那个东西起作用了。很抱歉问一个愚蠢的问题,但我对在 Mac 环境中进行开发很幼稚。 现在,我遇到另一个问题, 我想在终端上运行的应用程序会启动自己的小程序并打印到自己身上,可能不是终端。因为当我执行它时无法从控制台读取任何内容。但是,当我实际进入终端并运行程序时,我可以在那里看到输出。 所以,我正在尝试将所有输出分割到一个文件中,并代替读取该文件。但是,由 Java 代码创建的文件为空。 - Saurabh Lalwani
为什么您要两次问同一个问题,http://stackoverflow.com/questions/2875085/execute-external-program-from-java - phoenix24
不一样,有些微小的差别。我只是想清楚我想要什么,所以我提出了一个新问题。 - Saurabh Lalwani
3个回答

8
为了使重定向按照所写的方式工作,您需要执行以下操作:
Process p = Runtime.getRuntime().exec(
      new String[]{"/bin/bash", "-c", "ls > OutputFileNames.txt"});

你遇到的问题是Runtime.exec(String)将命令行按参数简单分割的方式。
如果你想在Shell提示符下运行该命令(就像现在这样),你需要输入以下内容:
$ /bin/bash -c "ls > OutputFileNames.txt"

因为"bash"的"-c"选项要求生成的shell命令行作为单个shell参数。但是,如果你将裸引号放入Java字符串中,Runtime.exec(String)方法仍然会错误地分割。唯一的解决方案是将命令参数提供为数组。


Javadoc指出命令行被分割为多个部分:“命令参数被解析成标记,然后在单独的进程中执行。标记解析由StringTokenizer完成...”。请参见http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Runtime.html#exec%28java.lang.String%29 new StringTokenizer(command) - mdma
好的,我看到你的编辑了,你引用了bash命令。我没有注意到原始的命令行是错误的。 - mdma
谢谢Stephen。那个东西起作用了。很抱歉问一个愚蠢的问题,但我对在Mac环境下开发很幼稚。现在,我遇到另一个问题,我想在终端上运行的应用程序启动了自己的小程序并将其打印到自己上,而不是终端上。因为当我执行它时,我无法从控制台读取任何内容。但是,当我实际上去终端并运行程序时,我可以在那里看到输出。所以,我正在尝试将所有输出分裂到文件中,并代替读取该文件。但是,Java代码创建的文件再次为空。 - Saurabh Lalwani
@Saurabh,请提出一个单独的问题。请尽量让它更易于理解。 - Stephen C
@Saurabh,请查看我的回答。你不能同时将输出重定向到文件并从Java中读取进程输出。 - mdma
显示剩余2条评论

1

需要注意几个事情:

  • 检查程序所在的目录(通过相同的方式运行pwd),然后确保您有写入该目录的权限。
  • 查看文件OutputFileNames.txt是否已存在(并检查权限)。
  • 如果文件存在,则删除它并重新运行以查看其是否会重新创建,并查看其内容。
  • 尝试使用命令"/bin/bash -c 'ls > OutputFileNames.txt'"——可能是bash输出与ls输出不同(在您的示例中,重定向可能应用于bash而不是ls)。

1
问题出现的原因是进程的标准输出被重定向到文件中,所以Java的Process无法收集输出。
如果您想将输出写入文件,并在Java中可读取,则有两个选项:
  1. 保持重定向不变,然后从OutputFile.txt中读取列表,例如使用new FileReader("OutputFile.txt")。请注意,exec()是异步的,并且可能在进程完成写入文件之前返回,因此您需要等待进程退出,使用Process.waitFor()
  2. 删除重定向,并直接将列表读入Java(就像您正在做的那样)。然后,您可以使用FileWriter将此列表写入文件。
底线是您不能同时使用重定向和从进程读取输出 - 只能选择其中一个。
编辑:您的评论表明1)是首选方法。这将使用操作系统shell将输出写入文件,然后在进程退出时从Java中读取它。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接