Java提供了Runtime.exec()方法来启动一个新进程。该方法可以返回Process对象,通过该对象可以控制和管理子进程的输入、输出以及错误流。这个方法中的参数是一个字符串,它描述了一个shell命令,应该如何来运行这个新的子进程。
为了更好地使用exec()方法,在使用exec()的时候,我们应该学会:
1.正确处理进程输出
2.合并输出流,正确地处理错误
3.增加进程的交互
4.通常的编程技巧
下面我们将详细讲解一下这个问题。
一. 确定进程输出
一旦启动一个进程,子进程处理输出流,而主进程负责处理输入和错误。通常情况下,这意味着你应该在一个线程中完成一个任务,而其他的线程则负责读取和处理输出,确保没有线程会阻塞等待运行子进程的输出。
输出有时被缓冲,因此要求读取器定期刷新来将缓冲区中的数据充满。下面的代码展示了一个如何启动UNIX进程的例子:
import java.io.*;
public class Test {
public static void main(String[] args) throws IOException {
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("ls");
InputStream stdin = proc.getInputStream();
InputStreamReader isr = new InputStreamReader(stdin);
BufferedReader br = new BufferedReader(isr);
String line = null;
System.out.println("<OUTPUT>");
while ( (line = br.readLine()) != null)
System.out.println(line);
System.out.println("</OUTPUT>");
int exitVal = proc.waitFor();
System.out.println("Process exitValue: " + exitVal);
}
}
注意到我们启动子进程的时候并没有提供输出程序到主进程的管道。相反,子进程的输出流(在这种情况下是ls的输出)被start()方法连接到新的InputStreamReader,并连接可以传递到缓冲的BufferedReader。然后,我们部分读取由子进程生成的结果并将其显示在主进程的STDOUT上。
二. 合并输出流,正确处理错误
Java允许三种不同的标准进程流:
1.输入流:包括子进程对标准输入流(System.in)的使用。
2.输出流:包括子进程对标准输出流(System.out)和标准错误流(System.err)的使用。
3.错误流:该流用于报告错误,这些错误对于诊断问题很有用。
大多数情况下,我们应该为每种类型的流创建单独的进程来处理特定的工作。但也有时候,显式地为子进程指定流已经足够。这种方法特别适合于与系统进程通信的应用程序(例如,爬虫程序或者系统监视器)。
下面的代码演示了如何合并输出流,并分离错误流。
import java.io.*;
public class Test {
public static void main(String[] args) throws IOException {
Runtime rt = Runtime.getRuntime();
String[] cmd = {"ls", "-al"};
Process proc = rt.exec(cmd);
InputStream stdin = proc.getInputStream();
InputStreamReader isr = new InputStreamReader(stdin);
BufferedReader br = new BufferedReader(isr);
InputStream stderr = proc.getErrorStream();
InputStreamReader esr = new InputStreamReader(stderr);
BufferedReader ebr = new BufferedReader(esr);
String line = null;
System.out.println("<OUTPUT>");
while ( (line = br.readLine()) != null)
System.out.println(line);
System.out.println("</OUTPUT>");
System.out.println("<ERROR>");
while ( (line = ebr.readLine()) != null)
System.out.println(line);
System.out.println("</ERROR>");
int exitVal = proc.waitFor();
System.out.println("Process exitValue: " + exitVal);
}
}
三. 增加进程的交互
下列示例演示了如何启动一个交互式进程,并发送输入到用不同于按下Enter键的方式回答问题。下面的程序会启动iconv,该程序是一个Unix将不同字符编码转换成一种标准字符编码的命令行实用程序。
import java.io.*;
public class Test {
public static void main(String[] args) throws IOException {
Runtime rt = Runtime.getRuntime();
Process proc = rt.exec("iconv -f ISO-8859-1 -t UTF-8");
OutputStream out = proc.getOutputStream();
InputStream in = proc.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
PrintWriter writer = new PrintWriter(out);
String[] messages = {"food", "coat", "car"};
for (String message : messages) {
writer.println(message);
String line = reader.readLine();
System.out.println(line);
}
writer.println("quit");
writer.flush();
int exitVal = proc.waitFor();
System.out.println("Process exitValue: " + exitVal);
}
}
四. 通常的编程技巧
当使用ProcessBuilder,Runtime.exec()或System()等方法启动另一个的JVM时,也可以被称为Java调用时,我们需要使用Process.waitFor()等方法等待子进程完成。这里给出完整的示例:
import java.io.*;
public class Test {
public static void main(String[] args) throws IOException, InterruptedException {
String command = "java -cp ./example.jar com.mycompany.app.Main";
ProcessBuilder builder = new ProcessBuilder(command.split("\\s+"));
builder.directory(new File("/home/user/"));
builder.redirectOutput(new File("/home/user/output.txt"));
Process process = builder.start();
int exitValue = process.waitFor();
if (exitValue == 0) {
System.out.println("Process exited without errors");
} else {
System.out.println("Process exited with errors");
}
}
}
在这个示例中,我们使用ProcessBuilder来启动一个Java应用程序,该程序包含在example.jar中,并且该应用程序的Main类位于com.mycompany.app包中。操作目录被设置为/home/user路径。标准输出被重定向到一个名为output.txt的文件。最后在waitFor()方法中等待进程结束,如果进程完全正确执行将返回0,否则返回非0。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java.lang.Runtime.exec的左膀右臂:流输入和流读取详解 - Python技术站