java - 使用命令参数启动 native 终端 (Java)

标签 java linux macos terminal

我有一个看似微不足道的问题:我想从 java 进程启动终端并向终端发出一两个命令。 我有一个简单的示例代码,可以在带有 CMD 的 Windows 上完美运行。但我无法在 Linux 或 Mac OS 机器上实现完全相同的行为。 我知道该命令需要更改,但不幸的是我无法将一串参数传递到 Mac 上的终端。

这里是 Windows 的工作代码:

import java.lang.ProcessBuilder.Redirect;

public class ExecTest {
    public static void main(String[] args){ 
        String cmd = "cmd /c start cmd.exe /K \"echo hello && echo bye\"";
        try {
            Process p = Runtime.getRuntime().exec(cmd);
            p.waitFor();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在 lubuntu 上,我已经能够使用以下命令创建终端:

lxterminal -l -e 'echo hello && echo bye && read'

但这仅在由终端调用而不是通过 java 进程调用时才有效。

TLDR:此命令在 Linux 和 Mac 上的等效项是什么:

cmd /c start cmd.exe /K \"echo hello && echo bye\"

最佳答案

我建议您使用 ProcessBuilder 来受益于更简单的输出重定向以及在不使用线程的情况下使用它的能力,并将命令作为 String[] 而不是平面传递String 能够支持各种包装方法。如果您更愿意坚持使用 Runtime.exec(),它还支持 String[],但下面的示例使用 ProcessBuilder

static int executeInTerminal(String command) throws IOException, InterruptedException {
    final String[] wrappedCommand;
    if (isWindows) {
        wrappedCommand = new String[]{ "cmd", "/c", "start", "/wait", "cmd.exe", "/K", command };
    }
    else if (isLinux) {
        wrappedCommand = new String[]{ "xterm", "-e", "bash", "-c", command};
    }
    else if (isMac) {
        wrappedCommand = new String[]{"osascript",
                "-e", "tell application \"Terminal\" to activate",
                "-e", "tell application \"Terminal\" to do script \"" + command + ";exit\""};
    }
    else {
        throw new RuntimeException("Unsupported OS ☹");
    }
    Process process = new ProcessBuilder(wrappedCommand)
            .redirectErrorStream(true)
            .start();
    try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
        String line;
        while ((line = reader.readLine()) != null) {
            System.out.println(line); // Your superior logging approach here
        }
    }
    return process.waitFor();
}

在 3 个操作系统上进行了测试。此处未解释 3 个 boolean 值 is{Windows|Linux|Mac},因为操作系统检测是另一个主题,因此对于示例,我保持简单并在方法之外进行处理。

ProcessBuilder 配置为将 stderr 重定向到 stdout,以便需要读取单个流。然后读取并记录该流,因为您必须使用 stderr 和 stdout,以防终端本身打印内容(不是您在终端中运行的命令,这是关于终端本身打印的内容),如果它打印太多您存在使进程无限期阻塞、等待读取缓冲区的风险。 See this nice answer .

对于 macOS,如果您传递的命令始终是单个可执行脚本,您也可以使用 {"open", "-a", "Terminal", command},但这不起作用与echo hello && echo bye。同样,您可以使用 {"/Applications/Utilities/Terminal.app/Contents/MacOS/Terminal", command},这将使您运行应用程序的第二个实例,但具有相同的限制。

最后一点:您可以提供合理的基本实现,但您可能需要对其进行配置以允许替代终端应用程序(特别是在变化很大的 Linux 上)。

关于java - 使用命令参数启动 native 终端 (Java),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44032813/

相关文章:

node.js - Brew 清理错误 : Permission denied @ unlink_internal

java - 如何让 ScrolledComposite 中包含 FlowLayout 的 Composite 垂直而不是水平增长?

Java如何使用ListIterator作为队列?

ruby-on-rails - 我如何调试发送的 TERM 信号?

c++ - 交叉编译错误 "arm-none-eabi-g++ cannot find entry symbol"

C 代码适用于 Mac (Darwin 13.4),但不适用于 Linux (2.6.32)

Python GDAL 未安装在 Mac OSX El Capitan 上

java - JVM PermGen OOM 即使尚未达到 Max

java - onOptionsItemSelected 未从 fragment 内调用

c - 用户输入到 C 中的 linux 命令