【问题标题】:Java and MySQL Console REPLJava 和 MySQL 控制台 REPL
【发布时间】:2018-02-25 15:10:10
【问题描述】:

我需要编写一个 Java 代码来创建一个运行 MySQL 控制台命令的 shell,并且可以保留交互式会话,直到它被故意破坏。

为了模拟它,这是我的代码。

    ProcessBuilder pb;
    pb = new ProcessBuilder(
        "cmd.exe","/c","c://xampp//mysql//bin//mysql","-uroot");

    Process p = pb.start();

    OutputStream stdout = p.getOutputStream();
    InputStream stdin = p.getInputStream();

    BufferedReader reader = new BufferedReader(new InputStreamReader(stdin));
    BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(stdout));

    boolean written = false;
    while(true) {
        System.out.println("loop... ");

        writer.write("show databases;\r\n\r\n");
        writer.flush();
        writer.newLine();
        writer.newLine();
        writer.flush();
        writer.close(); //If not close, it cannot produce result

        String line;
        line = reader.readLine();
        while (line != null) {
            System.out.println(line);
            line = reader.readLine();
        }
    }

问题是,代码只运行一次,因为在循环中调用了writer.close()。但是如果我不打电话给writer.close(),它永远不会响应。

我怎样才能实现我的目标?

【问题讨论】:

  • 首先请说明从Java启动mysql的目的。而且,这个问题是针对 MySQL 还是针对 Java 的 ProcessBuilder?如果你启动 cat 而不是 mysql 会发生什么?
  • 从 Java 启动 MySQL 的目的是从 MySQL 控制台交互地发送命令和接收结果。不通过 JDBC 进行交互。最终目标实际上是为 MySQL 提示开发一个基于 REPL 特性的 web。希望我说清楚。
  • Java 会阻止顽皮的黑客说出DROP DATABASE mysql 吗?
  • 请记住,与 JDBC 相比,通过 cmd 和 mysql 的性能开销很大。
  • 嗨 Rick,没关系...将在容器上运行它以检测恶意代码,或者我们将对其进行清理。 REPL 目前是针对 MySQL 的,但稍后将使其成为其他类似的交互,例如:Python 解释器。

标签: java mysql read-eval-print-loop


【解决方案1】:

您的问题来自line = reader.readLine()。在找到完整的行之前它不会返回,这由输出流中的换行符指示。但是当你在处理来自mysql的输出时,你不会得到一个完整的行,而是一个“提示”,比如

mysql> _

此外,它永远不会返回null,直到它正在读取的进程终止或以其他方式关闭其输出流。因此,您不能将其用作循环终止条件。相反,您应该逐个字符地阅读流,寻找您提交的命令已完成的指示。

我没有安装 mysql,因为我安装了 sqlite3,所以我创建了一个脚本,它使用测试数据库“sql”启动它,查询表 t1(单文本列,两行),并打印输出。重复 3 次。

ProcessBuilder pb = new ProcessBuilder("/usr/bin/sqlite3", "sql");
Process p = pb.start();
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(p.getOutputStream()));

// Don't use a BufferedReader; it waits for '\n' characters.
InputStreamReader isr = new InputStreamReader(p.getInputStream());

// Our own "Buffered Reader" workspace 
StringBuilder sb = new StringBuilder();

// Execute 3 times only ...
for(int i=0; i<3; i++) {

   System.out.println("Command: select * from t1;");
   bw.write("select * from t1;\n");
   bw.write(".print !-DONE-!\n");
   bw.flush();

   System.out.println("Result:");
   // Repeat until magic phrase in read buffer
   while (true) {

      // Read characters one at a time...
      char ch = (char) isr.read();

      // Do our own newline processing.
      if (ch == '\n') {
         String line = sb.toString();   // Buffer to string
         sb.setLength(0);               // ... and clear buffer

         if (line.equals("!-DONE-!"))
            break;

         System.out.println(line);
      } else {
         sb.append(ch);
      }
   }
   System.out.println();
}

jshell pb.java 的输出:

Command: select * from t1;
Result:
Hello world
Goodbye world!

Command: select * from t1;
Result:
Hello world
Goodbye world!

Command: select * from t1;
Result:
Hello world
Goodbye world!

|  Welcome to JShell -- Version 9
|  For an introduction type: /help intro

jshell> _

似乎stdin 来自另一个进程,stdout 去往另一个进程,我的sqlite3 拒绝在stdout 上发出sqlite&gt; 提示。这让我很伤心,在与各种设置和命令行选项斗争了一个小时后,我放弃了,只是添加了一个打印命令,可以打印出神奇的字词!-DONE-!。输出处理会寻找这个魔法词,并跳出结果读取循环。

如果您能找出使mysqlstdout 上吐出mysql&gt; _ 提示符所需的魔法咒语,那么您的中断条件将位于非换行路径中,例如:

      } else {
         sb.append(ch);
         if (sb.length() == 7 && sb.toString().equals("mysql> ")) {
            sb.setLength(0);
            break;
         }
      }

【讨论】:

  • 嗨 AJNeufield,我尝试将它应用到 MySQL 控制台但无济于事。我仍然需要调用 close 方法。
  • 当我为我的结果读取循环处理中断条件时,我在循环中有System.out.println("[" + sb + "] ch="+((int)ch));。它帮助我弄清楚什么会回来。编辑您的问题并添加更新的代码(不要删除原始代码)以及调试语句产生的输出(您可以从输出中修剪一些行,但我绝对想看看它在遇到之前和之后输出什么换行符。
  • 嗨 AJNeufield,将尝试解决方案。但是会给你关于行为的详细解释的赏金,它让我对流程构建器有更多的了解以及解决它们的方法。
  • 感谢您的赏金。但请务必发布调试输出,如果你不能让它工作。我们还没有完成。除非我们在已解决您的问题并且也可以帮助其他人的有效答案旁边有一个绿色复选标记,否则此问题不会得到解答。
猜你喜欢
  • 2011-05-14
  • 2016-02-06
  • 2012-01-01
  • 2018-06-07
  • 2021-10-24
  • 1970-01-01
  • 2010-09-08
  • 2019-04-26
  • 1970-01-01
相关资源
最近更新 更多