使用命令参数启动本机终端(Java)

时间:2017-05-17 18:57:17

标签: java linux macos terminal

我有一个看似微不足道的问题:我想从java进程启动一个终端并给终端一个或两个命令。 我有一个简单的示例代码,可以在带有CMD的窗口上完美运行。但我无法在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\"

2 个答案:

答案 0 :(得分:1)

我建议你使用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个布尔值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上有很多变化的应用程序)。

答案 1 :(得分:0)

String[] cmd = {"echo", "hello", "&&", "echo", "hi"}; Runtime.getRuntime().exec(cmd);

有多种方法可以做到这一点,但这应该对你有用。 Mac上的可执行文件应在终端中自动运行。

可能类似于:How To Run Mac OS Terminal Commands From Java (Using Runtime?) 如果有什么,他们有一些方法在终端中运行脚本。