Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
process.waitFor();
Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
process.waitFor();
Câu trả lời:
Có nhiều lý do mà waitFor()
nó không quay trở lại.
Nhưng nó thường dẫn đến thực tế là lệnh được thực thi không thoát.
Điều này, một lần nữa, có thể có nhiều lý do.
Một lý do phổ biến là quá trình tạo ra một số đầu ra và bạn không đọc từ các luồng thích hợp. Điều này có nghĩa là quá trình bị chặn ngay sau khi bộ đệm đầy và chờ quá trình của bạn tiếp tục đọc. Đến lượt quá trình của bạn sẽ đợi quá trình khác kết thúc (điều này sẽ không xảy ra vì nó đang đợi quá trình của bạn, ...). Đây là một tình huống bế tắc cổ điển.
Bạn cần liên tục đọc từ luồng đầu vào của quy trình để đảm bảo rằng nó không bị chặn.
Có một bài viết hay giải thích tất cả những cạm bẫy Runtime.exec()
và chỉ ra những cách xung quanh chúng có tên "When Runtime.exec () sẽ không" (vâng, bài viết có từ năm 2000, nhưng nội dung vẫn được áp dụng!)
waitFor()
không quay lại.
Có vẻ như bạn không đọc kết quả trước khi đợi nó kết thúc. Điều này chỉ tốt nếu đầu ra không lấp đầy bộ đệm. Nếu có, nó sẽ đợi cho đến khi bạn đọc đầu ra, bắt-22.
Có lẽ bạn có một số lỗi mà bạn không đọc. Điều này sẽ khiến ứng dụng dừng lại và chờ Một cách đơn giản để giải quyết vấn đề này là chuyển hướng các lỗi đến đầu ra thông thường.
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();
pb.redirectError(new File("/dev/null"));
redirectError
chỉ có sẵn từ Java 1,7
Cũng từ tài liệu Java:
java.lang
Tiến trình lớp học
Bởi vì một số nền tảng gốc chỉ cung cấp kích thước bộ đệm giới hạn cho các luồng đầu vào và đầu ra tiêu chuẩn, việc không ghi kịp thời luồng đầu vào hoặc đọc luồng đầu ra của quy trình con có thể khiến quy trình con bị chặn và thậm chí là bế tắc.
Việc không xóa bộ đệm của luồng đầu vào (dẫn đến luồng đầu ra của quy trình con) khỏi Quy trình có thể dẫn đến chặn quy trình con.
Thử cái này:
Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((reader.readLine()) != null) {}
process.waitFor();
DefaultExecutor executor = new DefaultExecutor(); PumpStreamHandler pumpStreamHandler = new PumpStreamHandler(stdoutOS, stderrOS); executor.setStreamHandler(pumpStreamHandler); executor.execute(cmdLine);
:, trong đó stoutOS và stderrOS là những thứ BufferedOutputStream
mà tôi đã tạo để ghi ra các tệp thích hợp.
process.close()
. Nhưng khi tôi mở luồng đầu vào như đề xuất ở trên và đóng ngay lập tức - vấn đề sẽ biến mất. Vì vậy, trong trường hợp của tôi, Spring đã chờ đợi tín hiệu đóng luồng. Mặc dù tôi đang sử dụng Java 8 có thể đóng tự động.
Tôi muốn thêm một cái gì đó vào các câu trả lời trước nhưng vì tôi không có người đại diện để bình luận, tôi sẽ chỉ thêm một câu trả lời. Điều này hướng tới người dùng Android đang lập trình bằng Java.
Theo bài đăng từ RollingBoy, mã này gần như phù hợp với tôi:
Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((reader.readLine()) != null) {}
process.waitFor();
Trong trường hợp của tôi, waitFor () không được giải phóng vì tôi đang thực hiện một câu lệnh không có trả về ("ip adddr flush eth0"). Một cách dễ dàng để khắc phục điều này là chỉ cần đảm bảo rằng bạn luôn trả lại nội dung nào đó trong bảng sao kê của mình. Đối với tôi, điều đó có nghĩa là thực hiện như sau: "ip adddr flush eth0 && echo done". Bạn có thể đọc bộ đệm cả ngày, nhưng nếu không có gì được trả lại, chuỗi của bạn sẽ không bao giờ giải phóng sự chờ đợi của nó.
Hy vọng rằng sẽ giúp một ai đó!
process.waitFor()
cái treo nó là reader.readLine()
cái treo nếu bạn không có đầu ra. Tôi đã cố gắng sử dụng waitFor(long,TimeUnit)
thời gian chờ nếu có gì đó không ổn và phát hiện ra rằng đó là lỗi đã đọc. Mà làm cho phiên bản timeouted đòi hỏi thread khác để làm việc đọc ...
Có một số khả năng:
stdout
.stderr
.stdin
.Như những người khác đã đề cập, bạn phải sử dụng stderr và stdout .
So với các câu trả lời khác, kể từ Java 1.7, nó thậm chí còn dễ dàng hơn. Bạn không cần phải tự tạo các luồng để đọc stderr và stdout .
Chỉ cần sử dụng ProcessBuilder
và sử dụng các phương pháp redirectOutput
kết hợp với redirectError
hoặc 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();
Vì lý do tương tự, bạn cũng có thể sử dụng inheritIO()
để ánh xạ bảng điều khiển Java với bảng điều khiển ứng dụng bên ngoài như:
ProcessBuilder pb = new ProcessBuilder(appPath, arguments);
pb.directory(new File(appFile.getParent()));
pb.inheritIO();
Process process = pb.start();
int success = process.waitFor();
Bạn nên thử tiêu thụ đầu ra và lỗi trong cùng một thời gian
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");
}
Tôi nghĩ rằng tôi đã quan sát thấy một vấn đề tương tự: một số quy trình bắt đầu, dường như chạy thành công nhưng không bao giờ hoàn thành. Hàm waitFor () đã đợi mãi mãi ngoại trừ trường hợp tôi đã giết quá trình trong Trình quản lý tác vụ.
Tuy nhiên, mọi thứ hoạt động tốt trong trường hợp độ dài của dòng lệnh là 127 ký tự hoặc ngắn hơn. Nếu tên tệp dài là không thể tránh khỏi, bạn có thể muốn sử dụng các biến môi trường, điều này có thể cho phép bạn giữ chuỗi dòng lệnh ngắn. Bạn có thể tạo một tệp hàng loạt (sử dụng FileWriter) trong đó bạn đặt các biến môi trường của mình trước khi gọi chương trình bạn thực sự muốn chạy. Nội dung của một lô như vậy có thể giống như sau:
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%
Bước cuối cùng là chạy tệp hàng loạt này bằng Runtime.
Đây là một phương pháp phù hợp với tôi. LƯU Ý: Có một số mã trong phương pháp này có thể không áp dụng cho bạn, vì vậy hãy thử và bỏ qua nó. Ví dụ: "logStandardOut (...), git-bash, v.v.".
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
// /programming/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();
}
Việc đọc luồng không đồng bộ kết hợp với việc tránh Chờ với thời gian chờ sẽ giải quyết được vấn đề.
Bạn có thể tìm thấy trang giải thích điều này tại đây http://simplebasics.net/.net/process-waitforexit-with-a-timeout-will-not-be-able-to-collect-the-output-message/