我正在尝试在java中实现一个shell,作为要求的一部分,我只能使用ProcessBuilder类来重定向IO。不允许进程IO流的线程或轮询。我有一切正常,但我似乎无法让管道(|)正常工作。我目前正在实现它们的方法是为每个子命令创建一个不同的ProcessBuilder,然后将每个子命令的输入设置为它之前的输出,并将管道的头部和尾部链接到shell的继承IO流。这工作正常,但某些进程(特别是grep)不会终止,并且在以这种方式使用时不会输出任何内容。 Grep在没有管道(grep -i)的情况下正常工作,但是当在管道中运行时(ls | grep txt)它永远不会终止,尽管ls已经完成(这可以通过检查shell的进程树来验证)。
我理解正常的解决方案是轮询每个进程的IO流并在进入时复制字节,或创建一个包装类来创建一个线程来为您完成此操作。不幸的是,我不允许使用任何一种方法进行此分配,因此我遇到了ProcessBuilder。
谷歌搜索让我无处可去,所以任何帮助都将不胜感激!以下是相关代码:
/*
Runs a pipeline of commands. Each String argument is a command to execute and pipe to the next.
*/
private static void runExternalMulti(String[] cmds) {
//array of process builders in order of pipe
ProcessBuilder[] builders = new ProcessBuilder[cmds.length];
for (String cmd : cmds) {
cmd = cmd.trim();
for (int i = 0; i < builders.length; i++) {
builders[i] = createProcessBuilder(cmd.split("\\s+"));
}
}
//set ProcessBuilder pipes
//skip last index
for (int i = 0; i < builders.length - 1; i++) {
ProcessBuilder first = builders[i];
ProcessBuilder next = builders[i+1];
first.redirectOutput(next.redirectInput());
next.redirectInput(first.redirectOutput());
}
//start each process and hold in array
Process[] processes = new Process[builders.length];
for (int i = 0; i < builders.length; i++) {
processes[i] = builders[i].start();
}
//loop until all process have finished
boolean running = true;
while (running) {
running = false;
for (Process process : processes) {
if (process.isAlive()) {
running = true;
}
}
//pause to avoid churning CPU
Thread.sleep(10);
}
}
/*
Creates a processBuilder for the command specified in parts. Each string is a space-separated part of the command line, with the first being the executable to run.
*/
private static ProcessBuilder createProcessBuilder(String[] parts) {
ProcessBuilder builder = new ProcessBuilder();
builder.directory(currentDirectory); //set working directory
//set process to share IO streams with java shell
builder.redirectInput(ProcessBuilder.Redirect.INHERIT);
builder.redirectOutput(ProcessBuilder.Redirect.INHERIT);
builder.redirectError(ProcessBuilder.Redirect.INHERIT);
//parse each token and pull out IO redirection and background characters
List<String> command = new ArrayList<>(parts.length);
for (int i = 0; i < parts.length; i++) {
//Ignore the background character, it has been read or ignored by the calling function
if (i == parts.length - 1 && "&".equals(parts[i])) {
continue; //don't parse an ending "&" as a command parameter
}
String str = parts[i];
//redirect to a file
if (">".equals(str)) {
if (i < parts.length - 1) {
File outFile = new File(currentDirectory, parts[i + 1]);
builder.redirectOutput(outFile);
builder.redirectError(outFile);
}
i++; //make sure to skip the redirected file name
//read in from a file
} else if ("<".equals(str)) {
if (i < parts.length - 1) {
File inFile = new File(currentDirectory, parts[i + 1]);
if (inFile.isFile()) {
builder.redirectInput(inFile);
}
}
i++; //make sure to skip the redirected file name
} else {
command.add(parts[i]);
}
}
builder.command(command);
return builder;
}
答案 0 :(得分:0)
管道是命令行解释器的功能。所以你必须选择:
bash -c
中(因此您将拥有带有2个参数的命令bash
)如果我理解正确你不能使用后者,但也许首先允许?