【问题标题】:How can execute command R CMD Rserve from Java如何从 Java 执行命令 R CMD Rserve
【发布时间】:2021-08-26 11:27:10
【问题描述】:

我正在尝试使用 Runtime exec 函数从我的 java 后端启动 Rserve。我通常可以执行像 killall 这样的 linux 命令来停止进程,但我无法启动它。 您可以使用此命令从命令行启动 Rserve

R CMD Rserve

并且工作正常。但是来自java:

Process pr = Runtime.getRuntime().exec("R CMD Rserve")

它不工作。那么我该如何从java中使用这个命令呢?

【问题讨论】:

  • “它不工作”是什么意思?怎么了?你安装了 RServe 吗?如果您在命令行上执行相同的命令,它是否有效?您是否检查过 PATH 设置是否正确?等
  • 是的,正如我在消息“R CMD Rserve”中所说,从命令行工作并启动 Rserve。但这不适用于使用 Java 中的相同命令。

标签: java r bash rserve


【解决方案1】:

我查看了一些文档并找到了一些东西。试试这个:

try {
    String command ="R CMD Rserve";
    BufferedWriter out = new BufferedWriter(new FileWriter(new File("path/to/file"), true));
    final Process process = Runtime.getRuntime().exec(command);
    BufferedReader buf = new BufferedReader(new InputStreamReader(process.getInputStream()));
    String line;
    while ((line = buf.readLine()) != null) {
        out.write(line);
        out.newLine();
    }
    buf.close();
    out.close();
    int returnCode = process.waitFor();
    System.out.println("Return code = " + returnCode);
    } catch (Exception e) {
        e.printStackTrace();
    }

如果有帮助,请告诉我:)

【讨论】:

  • 这与我使用的相同。唯一的区别是我打印命令的输出而不是写入文件。另外,我注意到 waitfor 函数的返回码,而我的返回码是“2”。任何不同于 0 的值都是命令的异常终止。
【解决方案2】:

exec 与“当我在 shell 中键入时它起作用”不同。毕竟,当您在“命令行”上键入它时,“命令行”本身就是一个应用程序。一般是/bin/bash,但是shell很多很多。

shell 不只是将您键入的内容逐字逐句扔到操作系统上,然后转到“这里”。运行这个'。它会接收您输入的内容并对其进行按摩。对其应用变换。 然后,被按摩的东西被扔到操作系统上,“在这里,运行这个”。

Java 与此类似,它接收您编写的内容,对其进行按摩,然后将其扔给操作系统。

这些按摩完全不同

例如,/bin/bash 会将*.txt 变成运行ls *.txt 的结果。 Java 不这样做,所以如果你尝试运行例如exec("/bin/ls *.txt") 在java中,它不起作用。 ls 试图找到一个字面上称为*.txt 的文件,并报告它找不到它。

诀窍是了解 bash 对您的输入所做的所有许多事情,并且不要依赖这些。 java 对按摩的实际作用是依赖于操作系统的,所以也不要依赖它。因此,一些规则:

  1. 始终使用绝对路径。不要运行R CMD Rserve。例如,运行/usr/local/share/R/bin/R CMD RServe。我不知道R 在哪里,但它是您系统某处的可执行文件。找到绝对路径; bash 所做的一项按摩是应用$PATH。 Java 可能应用路径,也可能不应用路径,谁知道你的路径设置是什么,以及 java 进程是否具有与你的 bash 相同的 PATH 设置;它可能不会,所以,绝对不要依赖它,并且总是,总是,写一个绝对路径。

  2. 注意 bash 内部结构,切勿使用它们。 lsps 通常是内部人员;根据定义,java 不能运行它们。如果您必须依赖 bashism,则必须实际运行 bash。不是exec("ls"),是exec("/bin/bash", "-c", "ls")

  3. 永远不要依赖 java 的空格分割能力。不要使用exec(cmd),也不要使用Runtime.getRuntime().exec。考虑这些被禁止的方法。创建一个ProcessBuilder,并始终使用List<String> 变体(列表中的第一个条目是命令的绝对 路径,其余的是命令行参数。

这会让你:

// to stop

List<String> args = List.of("/bin/bash", "-c", "killall -9 RServe");
ProcessBuilder pb = new ProcessBuilder(args);
// if you need to read what bash produces, config that here.
pb.start();

// to start
List<String> args = List.of("/abs/path/to/R", "CMD", "RServe");
ProcessBuilder pb = new ProcessBuilder(args);
// if you need to read what bash produces, config that here.
pb.start();

当然,除非您的 java 进程是启动它的进程,否则您将无法kill -9,因此很可能无法从 java 执行此操作 - 您只是没有权限。

【讨论】:

    【解决方案3】:

    我让它改变了我试图调用该过程的方式。 rzwitserloot 的回答对于解决这个问题非常有用。 我不知道“R CMD”命令的内部结构,但它不喜欢 java 进程构建器。 还有其他方法可以在 R 控制台中调用 Rserver 守护程序,所以我尝试这样做。这工作正常:

    List<String> args = List.of("/bin/sh", "-c","echo 'library(Rserve);Rserve(args=\"--no-save --slave\");'| /usr/lib/R/bin/R --no-save --slave");
            ProcessBuilder pb = new ProcessBuilder(args);
            Process p = pb.start();
    

    【讨论】: