尝试从java程序中执行shell命令时,我遇到了一个奇怪的问题。由于存在数千个解释如何操作的网站,因此我使用了以下推荐代码:
public String executeShellCommand (String command)
{
try
{
StringBuffer sb = new StringBuffer();
String line = "";
Process p = Runtime.getRuntime().exec(command);
BufferedReader reader = new BufferedReader(new InputStreamReader(
p.getInputStream()));
while ((line = reader.readLine()) != null)
sb.append(line + "\n");
p.waitFor();
return sb.toString();
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
实际上,当我尝试执行ls -aF
实例时工作正常,我得到了一些输出结果。因此,我非常确定上述代码原则上是正确的。但是,我有另一个程序,我想运行并生成一个文件作为输出。我想以上述方式执行它,但它永远不会执行,也不会生成输出文件。此外,我没有在java中收到任何错误,警告或任何内容。将实际的command
参数字符串复制并粘贴到控制台中时,直接在shell中执行programm / command工作正常,并生成输出文件。所以我传递给方法的命令也是正确的。
尝试从java中执行shell命令时还需要注意其他事项吗?
更新:我根据建议修改了我的代码。但是,它仍然悬而未决:
public String executeShellCommand(List<String> command, String logfile, boolean waitForProcess) { try {
ProcessBuilder pb = new ProcessBuilder(command);
System.out.println("pb.toString() = " + pb.toString());
Process p = pb.start();
System.out.println("2");
BufferedReader err = new BufferedReader(new InputStreamReader(p.getErrorStream()));
BufferedReader out = new BufferedReader(new InputStreamReader(p.getInputStream()));
System.out.println("3");
StringBuilder errSb = new StringBuilder();
StringBuilder outSb = new StringBuilder();
String line;
System.out.println("4");
while ((line = err.readLine()) != null) { // <--- code hangs here
errSb.append(line + "\n");
System.out.println("errSb = " + errSb.toString());
}
System.out.println("4a");
while ((line = out.readLine()) != null) {
outSb.append(line + "\n");
System.out.println("outSb = " + outSb.toString());
}
System.out.println("5");
if(waitForProcess) {
System.out.println("Wait for process");
p.waitFor();
} else {
System.out.println("Sleep 5000");
Thread.sleep(5000);
}
System.out.println("6");
//Log result to file
if(logfile != null) {
OutputStreamWriter outWriter = new OutputStreamWriter(new FileOutputStream(logfile));
outWriter.write(errSb.toString());
outWriter.close();
}
return errSb.toString();
} catch(Exception e) { e.printStackTrace(); } return null; }
答案 0 :(得分:1)
如果您的命令向stderr写入太多字符,这将阻止。与sdtout类似,Java通过管道重定向stderr,如果你不读取管道,它可以填满并阻塞(管道的大小可能小于256字节)。为了避免这种情况,你需要从Process.getErrorStream()读取,最好从另一个线程读取,因为主线程忙于从Process.getInputStream()读取。
避免这种情况的一种简单方法是使用ProcessBuilder类而不是Runtime.exec()和ProcessBuilder.redirectErrorStream(true),以便将stdout和stderr合并到Process.getInputStream()
答案 1 :(得分:0)
由于某些本机平台仅为标准输入和输出流提供有限的缓冲区大小,因此无法及时写入输入流或读取子进程的输出流可能导致子进程阻塞甚至死锁。
您正在呼叫p.waitFor()
。如果我们仔细阅读waitFor() documentation:
如果需要,使当前线程等待,直到此Process对象表示的进程终止。
您正在等待挂起的进程,因为它的错误流和输出流永远不会被读取。
你应该做的是阅读这些流:
p.start();
BufferedReader err= new BufferedReader(new InputStreamReader(p.getErrorStream()));
BufferedReader out = new BufferedReader(new InputStreamReader(p.getOutputStream()));
StringBuilder errSb = new StringBuilder();
StringBuilder outSb = new Stringbuilder();
String line;
while ((line = err.readLine()) != null) {
errSb.append(line);
}
while ((line = out.readLine()) != null) {
outSB.append(line);
}
int retCode = p.waitFor(); //0 for success
System.out.println(retCode);
System.err.println(errSB.toString());
在通过Process
课程调用外部程序时,您应该始终阅读错误流,否则您可能会发现自己处于这种奇怪的情况,即进程永远挂起。 (直到其他人 - 操作系统,另一个应用程序等 - 更准确地杀死它)。
我还注意到你使用的Runtime.getRuntime()
不是推荐的方式来运行外部程序,从java 1.5开始,按照javadoc:
从1.5开始,ProcessBuilder.start()是创建流程的首选方式。
ProcessBuilder pb = new ProcessBuilder("ls" , "-aF");
Process p = pb.start();