Java和MySQL控制台REPL

Java和MySQL控制台REPL,java,mysql,read-eval-print-loop,Java,Mysql,Read Eval Print Loop,我需要编写一个Java代码来创建一个运行MySQLconsole命令的shell,并可以保留交互式sesion,直到它被故意销毁 为了模拟它,这是我的代码 ProcessBuilder pb; pb = new ProcessBuilder( "cmd.exe","/c","c://xampp//mysql//bin//mysql","-uroot"); Process p = pb.start(); OutputStream stdout =

我需要编写一个Java代码来创建一个运行
MySQL
console命令的shell,并可以保留交互式sesion,直到它被故意销毁

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

    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()
,它将永远不会响应


如何实现我的目标?

您的问题来自
line=reader.readLine()
。直到找到一个完整的行(由输出流中的换行符指示),它才会返回。但是,当您处理来自
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();
}
似乎由于
stdin
来自另一个进程,并且
stdout
将进入另一个进程,我的
sqlite3
拒绝在
stdout
上发出
sqlite>
提示。这让我非常伤心,在与各种设置和命令行选项进行了一个小时的斗争后,我放弃了,只添加了一个打印命令,它可以打印神奇的单词
-完成-。输出处理寻找这个神奇的字,并中断结果读取循环

如果您能找出使
mysql
stdout
上弹出
mysql>提示所需的魔法咒语,那么您的中断条件将在非换行路径中,类似于:

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

首先,请解释从Java启动
mysql
的目的。问题是针对MySQL还是针对Java的ProcessBuilder?如果启动
cat
而不是
mysql
,会发生什么?从Java启动mysql的目的是以交互方式从mysql控制台发送命令并接收结果。不要通过JDBC进行交互。最终的目标实际上是开发一个基于web的MySQL提示符,其中包含REPL特性。希望我能说清楚。Java能阻止顽皮的黑客说
删除数据库mysql
?记住,与JDBC相比,通过cmd和mysql的性能开销非常大。嗨,瑞克,没关系。。。将在容器上运行恶意代码,或者我们将对其进行清理。REPL现在是针对MySQL的,但以后会变成其他类似的交互,例如:Python解释器。我仍然需要调用close方法。当我在努力解决结果读取循环的中断条件时,我有
System.out.println(“[”+sb+“]ch=“+((int)ch))在循环中。它帮助我弄清楚什么会回来。编辑您的问题并添加更新的代码(不要删除原始代码)以及调试语句生成的输出(您可以从输出中修剪一些行,但我确实希望看到它在遇到换行之前和之后输出的内容。嗨,AJNeufield,我将尝试解决方案。但是,我将为您提供有关行为的详细解释的奖励,这将使我对processbuilder有更多的了解,并提供解决问题的方法。谢谢您的奖励y、 但是,如果您无法使调试输出正常工作,请发布调试输出。我们还没有完成。直到在解决您的问题并可以帮助其他人的工作答案旁边有一个绿色复选标记,此问题才得到回答。
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> _
      } else {
         sb.append(ch);
         if (sb.length() == 7 && sb.toString().equals("mysql> ")) {
            sb.setLength(0);
            break;
         }
      }