Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
process.waitFor();
waitFor()
不回来的原因有很多。
但这通常归结为执行的命令没有退出。
这可能有很多原因。
一个常见的原因是该过程产生了一些输出,而您没有从适当的流中读取。这意味着一旦缓冲区已满,进程就会被阻塞,并等待您的进程继续读取。您的进程又等待另一个进程完成(它不会,因为它等待您的进程,...)。这是典型的僵局情况。
您需要不断读取进程输入流以确保它不会阻塞。
有一篇很好的文章解释了
Runtime.exec()
的所有陷阱,并展示了解决它们的方法,名为 “当 Runtime.exec() 不会”(是的,这篇文章是 2000 年的,但内容仍然适用!)
看来您在等待输出完成之前没有读取输出。仅当输出未填满缓冲区时才可以。如果是,它将等待,直到您读取输出,catch-22。
也许您有一些您没有阅读的错误。这将导致应用程序停止并 waitFor 永远等待。解决这个问题的一个简单方法是将错误重定向到常规输出。
ProcessBuilder pb = new ProcessBuilder("tasklist");
pb.redirectErrorStream(true);
Process process = pb.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null)
System.out.println("tasklist: " + line);
process.waitFor();
同样来自 Java 文档:
java.lang
上课流程
因为一些原生平台只为标准输入提供有限的缓冲区大小, 输出流,未能及时写入输入流或读取输出流 子进程可能会导致子进程阻塞,甚至死锁。
无法清除输入流的缓冲区(通过管道传输到子进程的输出流) from Process 可能会导致子进程阻塞。
试试这个:
Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((reader.readLine()) != null) {}
process.waitFor();
我想在之前的答案中添加一些内容,但由于我没有代表发表评论,所以我只会添加一个答案。这是针对使用 Java 编程的 Android 用户。
根据 RollingBoy 的帖子,这段代码几乎对我有用:
Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((reader.readLine()) != null) {}
process.waitFor();
就我而言,waitFor() 没有释放,因为我正在执行一条没有返回的语句(“ip adddrlush eth0”)。解决此问题的一个简单方法是确保您始终在声明中返回某些内容。对我来说,这意味着执行以下命令:“ip adddrlush eth0 && echo done”。您可以整天读取缓冲区,但如果没有返回任何内容,您的线程将永远不会释放其等待。
希望对某人有帮助!
有几种可能:
stdout
上的所有输出。stderr
上的所有输出。stdin
。正如其他人提到的,你必须消耗 stderr 和 stdout。
与其他答案相比,从 Java 1.7 开始,它变得更加简单。您不必再自己创建线程来读取 stderr 和 stdout。
ProcessBuilder
并将方法 redirectOutput
与 redirectError
或 redirectErrorStream
结合使用。
String directory = "/working/dir";
File out = new File(...); // File to write stdout to
File err = new File(...); // File to write stderr to
ProcessBuilder builder = new ProcessBuilder();
builder.directory(new File(directory));
builder.command(command);
builder.redirectOutput(out); // Redirect stdout to file
if(out == err) {
builder.redirectErrorStream(true); // Combine stderr into stdout
} else {
builder.redirectError(err); // Redirect stderr to file
}
Process process = builder.start();
出于同样的原因,您还可以使用
inheritIO()
将 Java 控制台与外部应用程序控制台映射,例如:
ProcessBuilder pb = new ProcessBuilder(appPath, arguments);
pb.directory(new File(appFile.getParent()));
pb.inheritIO();
Process process = pb.start();
int success = process.waitFor();
您应该尝试同时消耗输出和错误
private void runCMD(String CMD) throws IOException, InterruptedException {
System.out.println("Standard output: " + CMD);
Process process = Runtime.getRuntime().exec(CMD);
// Get input streams
BufferedReader stdInput = new BufferedReader(new InputStreamReader(process.getInputStream()));
BufferedReader stdError = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String line = "";
String newLineCharacter = System.getProperty("line.separator");
boolean isOutReady = false;
boolean isErrorReady = false;
boolean isProcessAlive = false;
boolean isErrorOut = true;
boolean isErrorError = true;
System.out.println("Read command ");
while (process.isAlive()) {
//Read the stdOut
do {
isOutReady = stdInput.ready();
//System.out.println("OUT READY " + isOutReady);
isErrorOut = true;
isErrorError = true;
if (isOutReady) {
line = stdInput.readLine();
isErrorOut = false;
System.out.println("=====================================================================================" + line + newLineCharacter);
}
isErrorReady = stdError.ready();
//System.out.println("ERROR READY " + isErrorReady);
if (isErrorReady) {
line = stdError.readLine();
isErrorError = false;
System.out.println("ERROR::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::" + line + newLineCharacter);
}
isProcessAlive = process.isAlive();
//System.out.println("Process Alive " + isProcessAlive);
if (!isProcessAlive) {
System.out.println(":::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: Process DIE " + line + newLineCharacter);
line = null;
isErrorError = false;
process.waitFor(1000, TimeUnit.MILLISECONDS);
}
} while (line != null);
//Nothing else to read, lets pause for a bit before trying again
System.out.println("PROCESS WAIT FOR");
process.waitFor(100, TimeUnit.MILLISECONDS);
}
System.out.println("Command finished");
}
我想我观察到了类似的问题:一些进程启动了,似乎运行成功但从未完成。函数 waitFor() 一直在等待,除非我在任务管理器中杀死了该进程。
然而,当命令行长度为 127 个字符或更短时,一切都运行良好。如果长文件名不可避免,您可能需要使用环境变量,这可以让您保持命令行字符串简短。您可以生成一个批处理文件(使用 FileWriter),在调用实际要运行的程序之前在其中设置环境变量。
这样一批的内容可能如下所示:
set INPUTFILE="C:\Directory 0\Subdirectory 1\AnyFileName"
set OUTPUTFILE="C:\Directory 2\Subdirectory 3\AnotherFileName"
set MYPROG="C:\Directory 4\Subdirectory 5\ExecutableFileName.exe"
%MYPROG% %INPUTFILE% %OUTPUTFILE%
最后一步是使用运行时运行此批处理文件。
这是一个适合我的方法。 注意:此方法中的一些代码可能不适用于您,因此请尝试忽略它。例如“logStandardOut(...)、git-bash 等”。
private String exeShellCommand(String doCommand, String inDir, boolean ignoreErrors) {
logStandardOut("> %s", doCommand);
ProcessBuilder builder = new ProcessBuilder();
StringBuilder stdOut = new StringBuilder();
StringBuilder stdErr = new StringBuilder();
boolean isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows");
if (isWindows) {
String gitBashPathForWindows = "C:\\Program Files\\Git\\bin\\bash";
builder.command(gitBashPathForWindows, "-c", doCommand);
} else {
builder.command("bash", "-c", doCommand);
}
//Do we need to change dirs?
if (inDir != null) {
builder.directory(new File(inDir));
}
//Execute it
Process process = null;
BufferedReader brStdOut;
BufferedReader brStdErr;
try {
//Start the command line process
process = builder.start();
//This hangs on a large file
// https://stackoverflow.com/questions/5483830/process-waitfor-never-returns
//exitCode = process.waitFor();
//This will have both StdIn and StdErr
brStdOut = new BufferedReader(new InputStreamReader(process.getInputStream()));
brStdErr = new BufferedReader(new InputStreamReader(process.getErrorStream()));
//Get the process output
String line = null;
String newLineCharacter = System.getProperty("line.separator");
while (process.isAlive()) {
//Read the stdOut
while ((line = brStdOut.readLine()) != null) {
stdOut.append(line + newLineCharacter);
}
//Read the stdErr
while ((line = brStdErr.readLine()) != null) {
stdErr.append(line + newLineCharacter);
}
//Nothing else to read, lets pause for a bit before trying again
process.waitFor(100, TimeUnit.MILLISECONDS);
}
//Read anything left, after the process exited
while ((line = brStdOut.readLine()) != null) {
stdOut.append(line + newLineCharacter);
}
//Read anything left, after the process exited
while ((line = brStdErr.readLine()) != null) {
stdErr.append(line + newLineCharacter);
}
//cleanup
if (brStdOut != null) {
brStdOut.close();
}
if (brStdErr != null) {
brStdOut.close();
}
//Log non-zero exit values
if (!ignoreErrors && process.exitValue() != 0) {
String exMsg = String.format("%s%nprocess.exitValue=%s", stdErr, process.exitValue());
throw new ExecuteCommandException(exMsg);
}
} catch (ExecuteCommandException e) {
throw e;
} catch (Exception e) {
throw new ExecuteCommandException(stdErr.toString(), e);
} finally {
//Log the results
logStandardOut(stdOut.toString());
logStandardError(stdErr.toString());
}
return stdOut.toString();
}
异步读取流并结合避免超时等待将解决该问题。
您可以在此处找到解释此内容的页面http://simplebasics.net/.net/process-waitforexit-with-a-timeout-will-not-be-able-to-collect-the-output-message/
public static void main(String[] args) throws PyException, IOException, InterruptedException
这些应该是抛出的异常