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();
คำตอบ:
มีหลายเหตุผลที่waitFor()
ไม่กลับมา
แต่โดยปกติแล้วคำสั่งที่ดำเนินการจะไม่หยุดทำงาน
อีกครั้งอาจมีหลายสาเหตุ
สาเหตุทั่วไปประการหนึ่งคือกระบวนการสร้างผลลัพธ์บางอย่างและคุณไม่ได้อ่านจากสตรีมที่เหมาะสม ซึ่งหมายความว่ากระบวนการจะถูกบล็อกทันทีที่บัฟเฟอร์เต็มและรอให้กระบวนการของคุณอ่านต่อ กระบวนการของคุณจะรอให้กระบวนการอื่นเสร็จสิ้น (ซึ่งจะไม่เป็นเช่นนั้นเพราะรอให้กระบวนการของคุณ ... ) นี่คือสถานการณ์การหยุดชะงักแบบคลาสสิก
คุณต้องอ่านจากสตรีมอินพุตของกระบวนการอย่างต่อเนื่องเพื่อให้แน่ใจว่าจะไม่ปิดกั้น
มีบทความดีๆที่อธิบายข้อผิดพลาดทั้งหมดRuntime.exec()
และแสดงวิธีการรอบ ๆ ที่เรียกว่า"เมื่อ Runtime.exec () จะไม่" (ใช่บทความนี้มาจากปี 2000 แต่เนื้อหายังคงใช้ได้!)
waitFor()
ไม่ส่งคืน
ดูเหมือนว่าคุณไม่ได้อ่านผลลัพธ์ก่อนที่จะรอให้เสร็จสิ้น จะใช้ได้เฉพาะในกรณีที่เอาต์พุตไม่เติมบัฟเฟอร์ หากเป็นเช่นนั้นระบบจะรอจนกว่าคุณจะอ่านเอาต์พุต catch-22
บางทีคุณอาจมีข้อผิดพลาดบางอย่างที่คุณไม่ได้อ่าน ในกรณีนี้แอปพลิเคชันจะหยุดและรอเพื่อรอตลอดไป วิธีง่ายๆในการแก้ไขปัญหานี้คือกำหนดทิศทางข้อผิดพลาดไปยังเอาต์พุตปกติอีกครั้ง
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
มีให้เฉพาะตั้งแต่ Java 1.7
จาก Java doc ด้วย:
java.lang
กระบวนการชั้นเรียน
เนื่องจากแพลตฟอร์มเนทีฟบางแพลตฟอร์มให้ขนาดบัฟเฟอร์ที่ จำกัด สำหรับสตรีมอินพุตและเอาต์พุตมาตรฐานความล้มเหลวในการเขียนสตรีมอินพุตทันทีหรืออ่านสตรีมเอาต์พุตของกระบวนการย่อยอาจทำให้กระบวนการย่อยบล็อกและถึงขั้นหยุดชะงัก
การไม่ล้างบัฟเฟอร์ของอินพุตสตรีม (ซึ่งไปยังสตรีมเอาท์พุตของกระบวนการย่อย) จากกระบวนการอาจนำไปสู่การบล็อกกระบวนการย่อย
ลองสิ่งนี้:
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);
โดยที่ stoutOS และ stderrOS เป็นสิ่งBufferedOutputStream
ที่ฉันสร้างขึ้นเพื่อเขียนลงในไฟล์ที่เหมาะสม
process.close()
. แต่เมื่อฉันเปิดอินพุตสตรีมตามที่แนะนำข้างต้นและปิดทันทีปัญหาจะหายไป ดังนั้นในกรณีของฉัน Spring กำลังรอสัญญาณปิดสตรีม แม้ว่าฉันจะใช้ Java 8 auto closedable
ฉันต้องการเพิ่มบางอย่างในคำตอบก่อนหน้านี้ แต่เนื่องจากฉันไม่มีตัวแทนที่จะแสดงความคิดเห็นฉันจึงจะเพิ่มคำตอบ สิ่งนี้มุ่งเน้นไปที่ผู้ใช้ Android ที่กำลังเขียนโปรแกรมใน Java
ตามโพสต์จาก RollingBoy รหัสนี้เกือบจะใช้ได้กับฉัน:
Process process = Runtime.getRuntime().exec("tasklist");
BufferedReader reader =
new BufferedReader(new InputStreamReader(process.getInputStream()));
while ((reader.readLine()) != null) {}
process.waitFor();
ในกรณีของฉัน waitFor () ไม่ถูกปล่อยเนื่องจากฉันกำลังดำเนินการคำสั่งที่ไม่มีการส่งคืน ("ip adddr flush eth0") วิธีง่ายๆในการแก้ไขปัญหานี้คือเพียงให้แน่ใจว่าคุณส่งคืนบางสิ่งในใบแจ้งยอดของคุณเสมอ สำหรับฉันนั่นหมายถึงการดำเนินการต่อไปนี้: "ip adddr flush eth0 && echo done" คุณสามารถอ่านบัฟเฟอร์ได้ตลอดทั้งวัน แต่ถ้าไม่มีอะไรกลับมาเธรดของคุณจะไม่ปล่อยให้รอ
หวังว่าจะช่วยใครสักคน!
process.waitFor()
แฮงค์มันreader.readLine()
แฮงค์ถ้าคุณไม่มีเอาท์พุท ฉันพยายามใช้การwaitFor(long,TimeUnit)
หมดเวลาหากมีสิ่งผิดปกติเกิดขึ้นและพบว่ามันเป็นการอ่านที่หยุดชะงัก ซึ่งทำให้เวอร์ชันหมดเวลาต้องใช้เธรดอื่นในการอ่าน ...
มีความเป็นไปได้หลายประการ:
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%
ขั้นตอนสุดท้ายกำลังเรียกใช้ไฟล์แบตช์นี้โดยใช้ Runtime
นี่คือวิธีการที่เหมาะกับฉัน หมายเหตุ: มีรหัสบางอย่างในวิธีนี้ที่อาจใช้ไม่ได้กับคุณดังนั้นให้ลองเพิกเฉย ตัวอย่างเช่น "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
// /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();
}
การอ่านสตรีมแบบอะซิงโครนัสร่วมกับการหลีกเลี่ยงการรอด้วยการหมดเวลาจะช่วยแก้ปัญหาได้
คุณสามารถค้นหาหน้าอธิบายสิ่งนี้ได้ที่นี่http://simplebasics.net/.net/process-waitforexit-with-a-timeout-will-not-be-able-to-collect-the-output-message/