【问题标题】:Start native terminal with command arguments (Java)使用命令参数启动本机终端 (Java)
【发布时间】:2017-10-17 09:15:54
【问题描述】:

我有一个看似微不足道的问题:我想从一个 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\"

【问题讨论】:

  • 你真的想要一个“终端”吗?或者你只是想执行一些 shell 命令?
  • 我可以在我的 Ubuntu 机器上使用命令 'gnome-terminal' 和相同的 java 代码打开一个终端。你能试试这个吗?
  • @bmargulies 是的。提供一些上下文:我制作了一个代码编辑器,可以编译一些代码。我还创建了自己的终端来显示 exec 进程的结果。但在某些极端情况下,我无法完全显示 In 和 Outputstreams。因此,作为备份,我希望允许用户在本机终端中执行代码。所以是的,我想生成一个新的本地终端并执行一些代码。
  • @VipulGoyal 我不确定你的建议是什么。我知道我可以通过 java 进程打开一个终端,只需调用终端的名称,但主要问题是我无法向该终端提供任何命令。

标签: java linux macos terminal


【解决方案1】:

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

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

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

【讨论】:

  • 不幸的是,这不起作用。此外,链接的线程是关于执行自定义程序的,因为我试图启动终端并传递一些参数。
【解决方案2】:

我建议您使用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 上)。

【讨论】:

  • 只是为了验证,我们在同一页面上,您的代码是否调用终端并打开终端以便用户可以看到正在发生的事情并且打印到该终端? (我在问,因为您正在收集 Inputstream 并将其打印出来)
  • 天哪!您的代码确实有效!我正要走脚本路线,我必须创建可执行脚本和所有这些废话。我想补充一点:如何禁用可怕的垃圾邮件 Mac OS 进程终端? (诸如“上次登录:..”和“注销”、“保存会话”等)。另外,我还需要添加什么来为 Linux 命令提供“按任意按钮退出”?非常感谢您到目前为止的帮助:D
  • 抱歉,我不知道您是否可以通过编程方式控制该输出。也许终端应用程序中有选项。关于您的第一个问题,记录的输出是终端本身的输出,以防万一(macOS Terminal 在以这种方式调用时会打印 1 行)。
  • 对于xterm,除了窗口关闭按钮外,没有用户可以按下退出的按钮。你说的是钥匙吗?也许将; echo 'Press Enter to close...' && read 附加到命令
  • 好的,谢谢关于mac的事情。也许值得发布一个单独的问题? (我什至不知道我怎么能问这个:S)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-03
  • 2018-10-27
  • 1970-01-01
  • 2020-12-03
  • 2016-10-06
  • 1970-01-01
相关资源
最近更新 更多