用Java运行交互式shell程序
我正在尝试(但失败)解决如何在Java中运行一个完全交互式的shell程序 以下是场景: 我有一个大型GUI应用程序,它是跨平台的,用Java编写的。为此,我尝试添加一个用于运行headless的交互式命令行环境。事情的那一面都是美好的。但是,主GUI的功能之一是编辑文件。现在,对于命令行界面,我希望能够执行一个外部编辑器来编辑文件,然后在保存并退出后返回到原来的位置。例如,在Linux上,它可能执行“vi/path/to/file” 那么,如何执行该命令,使键盘和显示器与应用程序完全交互?我不想让它在后台运行。我不希望它通过Java重定向IO,我只希望一个命令“在前台”运行,直到它存在 就像我在C中使用了用Java运行交互式shell程序,java,exec,Java,Exec,我正在尝试(但失败)解决如何在Java中运行一个完全交互式的shell程序 以下是场景: 我有一个大型GUI应用程序,它是跨平台的,用Java编写的。为此,我尝试添加一个用于运行headless的交互式命令行环境。事情的那一面都是美好的。但是,主GUI的功能之一是编辑文件。现在,对于命令行界面,我希望能够执行一个外部编辑器来编辑文件,然后在保存并退出后返回到原来的位置。例如,在Linux上,它可能执行“vi/path/to/file” 那么,如何执行该命令,使键盘和显示器与应用程序完全交互?我不
system()
函数一样
到目前为止,我发现的所有东西都在后台运行命令,或者通过Java管道传输IO,这对于交互式(非行模式)应用程序是不起作用的
哦,还有最后一个警告:我仅限于Java1.6的兼容性,所以我无法使用ProcessBuilder
做一些奇特的事情
以下是强制性SSCCE:
class exetest {
public static void main(String[] args) {
try {
Process p = Runtime.getRuntime().exec("vi");
p.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
}
}
我希望Runtime.getRuntime().exec()会一直阻塞,直到我在vi
中完成为止,在这段时间内,所有键盘输入都会直接(RAW
)到vi
,所有屏幕输出都会直接返回给用户,不受影响
这就是我在C语言中实现它的方法:
void main() {
system("vi");
}
更新:
这达到了预期的效果,但a)仅限于Linux/OSX(问题不大,但最好是跨平台),b)是一个可怕的难题:
import java.io.*;
class exetest {
public static void main(String[] args) {
try {
Process p = Runtime.getRuntime().exec("/bin/bash");
OutputStream stdin = p.getOutputStream();
PrintWriter pw = new PrintWriter(stdin);
pw.println("vi < /dev/tty > /dev/tty");
pw.close();
p.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
}
}
import java.io.*;
类测试{
公共静态void main(字符串[]args){
试一试{
进程p=Runtime.getRuntime().exec(“/bin/bash”);
OutputStream stdin=p.getOutputStream();
PrintWriter pw=新的PrintWriter(标准输入法);
println(“vi/dev/tty”);
关闭();
p、 waitFor();
}捕获(例外e){
e、 printStackTrace();
}
}
}
首先,ProcessBuilder也在Java6中。如果你的意思是这个版本不充分,或者什么的,那么我想有两条信息你需要知道。首先,运行“外部编辑器”的主要方式可能是使用运行时类和exec方法。我怀疑你已经在做什么了?其次,您当前的GUI在某种程度上有一个“事件循环”,它是一个简单但主要的线程,只执行“等待事件”(如按键或“窗口事件”),将该事件分派给感兴趣的侦听器,然后等待下一个事件。按键和事件到外部编辑器,将不会通过此事件循环。。。无论如何,如果不使用更复杂的东西,比如OLE API,这并不容易。但是,正是“事件循环”让Java保持了活力和交互性,同时其他事情也在进行,比如在外部编辑器中编辑。我不确定您使用的是什么GUI框架,但大多数都已经有了这样的循环,您不必担心它。如果您是说在您想要的命令行模式下,您不希望正常的GUI运行,那么您基本上必须复制“事件循环”,首先启动该线程,然后调用runtime.exec方法。有道理吗
FWIW,如果您没有“源代码”来查看当前使用的GUI框架,如果您想查看一个,只是为了了解更多关于事件循环的信息,那么它的“显示”类中有一个,以及一些可能与您想要做的类似的示例。(注意,SWT可以“独立”运行,而不需要Eclipse的其余部分,以防您感到奇怪)
HTH我事先声明,这是一个可怕的混乱局面,但考虑到你的限制,我不确定你会找到更优雅的东西 而且,它在Windows上也不起作用 使用bash:
class Exetest {
public static void main(String[] args) {
try {
// Create a one-liner for bash
// Note that the trick is to do all the redirects and
// give the name of the file in a single argument.
String [] commandline = new String[3];
commandline[0] = "bash";
commandline[1] = "-c";
commandline[2] = "vi </dev/tty >/dev/tty test.txt";
Process p = Runtime.getRuntime().exec(commandline);
p.waitFor();
} catch (Exception e) {
e.printStackTrace();
}
}
}
类测试{
公共静态void main(字符串[]args){
试一试{
//为bash创建一行程序
//注意,诀窍是执行所有重定向和
//在单个参数中指定文件名。
String[]命令行=新字符串[3];
命令行[0]=“bash”;
命令行[1]=“c”;
命令行[2]=“vi/dev/tty test.txt”;
进程p=Runtime.getRuntime().exec(命令行);
p、 waitFor();
}捕获(例外e){
e、 printStackTrace();
}
}
}
通过这种方式,您可以在运行时编写实际的命令。与
系统(“vi”)
相当的精确的是:
final ProcessBuilder p = new ProcessBuilder("vi");
p.redirectInput(Redirect.INHERIT);
p.redirectOutput(Redirect.INHERIT);
p.redirectError(Redirect.INHERIT);
p.start().waitFor();
(我知道这要推迟3年,但对于其他有同样问题的人来说)你想要类似的东西吗?不,我不想要。我不希望Java与执行的程序交互,我希望用户与它交互。因此,他们键入
edit myfile
,它执行vi/path/to/myfile
,用户编辑文件就像他们在终端键入vi/path/to/myfile
一样。在CLI模式下运行时,没有GUI。GUI代码甚至还没有开始。当它这样做的时候,一切都在摇摆。没有GUI,就没有GUI事件循环。实际上,我正在为CLI接口使用JLine。6中的ProcessBuilder没有重定向类。你说得对,这是一个可怕的难题;)是的,它实现了预期的结果,但我不确定我是否能够接受这种实现。编辑器可执行文件将在运行时决定,我不确定您能否在一行中创建一个bash命令,包括重定向,因此它必须动态生成一个脚本以供执行。