我需要编写一个Java代码来创建一个运行MySQL
控制台命令的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()
,它就永远不会响应。
我如何实现目标?
答案 0 :(得分: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
拒绝在sqlite>
上发出stdout
提示}。这让我非常悲痛,经过一个小时的各种设置和命令行选项的斗争后,我放弃了,只是添加了一个打印命令,打印出神奇的单词!-DONE-!
。输出处理查找这个神奇的单词,并突破结果读取循环。
如果您能找出使mysql
吐出mysql> _
stdout
提示所需的神奇咒语,那么您的中断条件将位于非换行路径中,类似于:
} else {
sb.append(ch);
if (sb.length() == 7 && sb.toString().equals("mysql> ")) {
sb.setLength(0);
break;
}
}