对于学校项目,我正在尝试用Java创建一个终端。终端按以下方式工作:
编程抓取命令并用字符串
中的命令替换<command>
/bin/bash -c "cd current/directory/; <command>; echo kjsfdjkadhlga; pwd
程序启动通过ProcessBuilder
对象
kjsfdjkadhlga
字符串的输出,以便它知道用户的命令何时完成运行,然后抓取剩余的输出并将其存储为当前路径用户在。为了避免自己不得不实现我自己的输入解析器来处理线路上的多个命令,IO重定向以及与ProcessBuilder
一起使用的东西,我只是将命令转换为bash脚本让bash执行它。
由于每个进程只执行一个命令(或者在创建时给出的任何命令,在这种情况下是单个用户命令)然后终止,所以不存储特定于进程的信息,例如当前工作目录。要传输该信息,我在用户命令之后调用pwd,然后在下一个命令的过程中,但在运行用户命令之前,我cd
到该目录,有效地允许$PWD
的值在进程之间保持不变。
除了需要用户交互时,一切正常。如果用户只是键入cat
,它应该等待一行用户输入,然后打印,然后等待一行用户输入,然后打印,并永远重复(我不要处理 Crtl + C 然而......)。然而,实际发生的是终端等待一行用户输入,然后打印它,然后终止而不等待更多输入。
目前,我为正在运行的命令提供输入:
BufferedWriter stdin = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
stdin.write(input);
stdin.newLine();
stdin.close();
如果不是调用close()
,而是调用flush()
,那么cat
会等待用户输入并且在我终止终端程序之前没有做任何事情,此时它会打印用户输入的所有内容。
似乎flush()
功能实际上并没有做任何事情。使用原始OutputStream
并调用write()
而不是使用BufferedWriter
时提到的堆栈溢出问题。但是,这具有相同的效果。在OutputStream
的{{1}}文档中,它指出&#34; flush()
的flush方法不执行任何操作。&#34;
我也尝试使用OutputStream
,但文档说它的BufferedOutputStream
函数只是强制将缓冲的数据写入基础flush
,而不是OutputStream
改变OutputStream
没有刷新其流的事实。
This问题似乎是最有希望的问题,但在实施时我无法解决问题。这可能是因为我使用的是Mac OS而不是Windows。
如果保持stdin打开足够长的时间以提交多行输入,或者如果我说错了,有人知道怎么做吗?
Terminal terminal = new Terminal();
Scanner in = new Scanner(System.in);
while (in.hasNextLine())
{
String line = in.nextLine();
terminal.sendInput(line, terminal);
}
// ProcessReaderDelegate implements functions called when receiving output on stdout, stderr, and when the process terminates.
public int sendInput(String text, ProcessReaderDelegate delegate)
{
if (processes.size() > 0)
{
processes.get(0).sendInput(text); // Is a ProcessReader object
return 1;
}
run(text, delegate); // runs the given text as the <command> text described above
return 2;
}
public boolean sendInput(String input)
{
try
{
// stdin and process are a instance fields
// tried this and doesn't seem to work (with either flush or close)
stdin = new BufferedWriter(new OutputStreamWriter(process.getOutputStream()));
stdin.write(input);
stdin.newLine();
stdin.close();
// tried this and doesn't seem to work (with either flush or close)
//BufferedOutputStream os = new BufferedOutputStream(process.getOutputStream());
//os.write(input.getBytes());
//os.write("\n".getBytes());
//os.flush();
//os.close();
return true;
}
catch (IOException e)
{
System.out.println("ERROR: this should never happen: " + e.getMessage());
return false;
}
}
public void run(String command, ProcessReaderDelegate delegate)
{
// don't do anything with empty command since it screws up the command concatentaion later
if (command.equals(""))
{
delegate.receivedOutput(null, prompt);
return;
}
try
{
// create the command
List<String> list = new ArrayList<String>();
list.add(shellPath);
list.add(UNIX_BASED ? "-c" : "Command : ");
String cmd = (UNIX_BASED ? getUnixCommand(command) : getWindowsCommand(command));
list.add(cmd);
//System.out.println("command='" + list.get(0) + " " + list.get(1) + " " + list.get(2) + "'");
// create the process and run it
ProcessBuilder builder = new ProcessBuilder(list);
Process p = builder.start();
ProcessReader stdout = new ProcessReader(p, delegate, this);
new Thread(stdout).start();
processes.add(stdout);
}
catch (IOException e)
{
System.out.println(e.getMessage());
}
}
public void run()
{
try
{
boolean hitend = false;
String buffer = "";
while (true)
{
int c;
String text;
// ======================================================
// read from stdout
// read the next character
c = stdout.read();
// build the string
while (c != -1) // while data available in the stream
{
buffer += (char)c;
c = stdout.read();
}
// send the string to the delegate
if ((!hitend) && (buffer.length() > 0))
{
// END_STRING is the "kjsfdjkadhlga" echoed after the command executes
int index = buffer.indexOf(END_STRING);
if (index >= 0)
{
hitend = true;
text = buffer.substring(0, index);
buffer = buffer.substring(index + END_STRING.length());
if (outputDelegate != null)
{
outputDelegate.receivedOutput(process, text);
}
}
else
{
for (int i = END_STRING.length() - 1; i >= 0; i--)
{
index = buffer.indexOf(END_STRING.substring(0, i));
if (i == 0)
{
index = buffer.length();
}
if (index >= 0)
{
text = buffer.substring(0, index);
buffer = buffer.substring(index + i);
if (outputDelegate != null)
{
outputDelegate.receivedOutput(process, text);
}
}
}
}
}
// ======================================================
// read from stderr
// read the next character
c = stderr.read();
text = ""; // slow method; make faster with array
// build the string
while (c != -1) // while data available in the stream
{
text += (char)c;
c = stderr.read();
}
// send the string to the delegate
if ((text.length() > 0) && (outputDelegate != null))
{
outputDelegate.receivedError(process, text);
}
// ======================================================
// check if the process is done (and hence no more output)
boolean done = false;
try
{
int value = process.exitValue();
done = true; // if got to this point, then process is done
// read the ending environment variables
Map<String, String> env = new HashMap<String, String>();
String[] words = buffer.split(" ");
env.put(ENV_WORKING_DIR, words[0]);
if (envDelegate != null)
{
envDelegate.processTerminatedWithEnvironment(process, env);
}
// end the process
outputDelegate.processEnded(process);
stdout.close();
stderr.close();
break;
}
catch (Exception e) {System.out.println(e.getMessage());} // no exit value --> process not done
if (done) // just on the off chance that closing the streams crashes everything
{
break;
}
}
}
catch (IOException e)
{
System.out.println("ERROR: ProcessReader: " + e.getMessage());
}
}