在Java应用程序中执行Linux命令是一项常见的需求,特别是在系统管理、自动化运维或需要与操作系统交互的场景中,Java提供了多种方式来实现这一功能,每种方法都有其适用场景和优缺点,本文将详细介绍几种主流的实现方式,包括使用Runtime类、ProcessBuilder类,以及第三方库如Apache Commons Exec的使用方法,并探讨相关的最佳实践和注意事项。

使用Runtime类执行命令
Runtime类是Java中与操作系统交互最基础的方式,它提供了一个exec()方法来执行外部命令,执行简单的ls -l命令可以通过以下代码实现:
try {
Process process = Runtime.getRuntime().exec("ls -l");
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
int exitCode = process.waitFor();
System.out.println("Exit Code: " + exitCode);
} catch (Exception e) {
e.printStackTrace();
}
Runtime.exec()方法在处理复杂命令(如包含管道或重定向)时较为有限,且容易引发资源泄漏问题,因此不推荐在生产环境中直接使用。
使用ProcessBuilder类执行命令
ProcessBuilder类是Java 5引入的更强大、更灵活的替代方案,它允许更精细地控制进程的执行环境,执行带参数的命令并设置工作目录:

ProcessBuilder pb = new ProcessBuilder("ls", "-l");
pb.directory(new File("/path/to/directory"));
Process process = pb.start();
// 读取输出和错误流的代码与Runtime示例类似
ProcessBuilder的优势在于可以轻松管理命令的参数、环境变量和工作目录,并且支持输入/输出重定向,它还提供了redirectErrorStream(true)方法将错误流合并到输出流中,简化了处理逻辑。
处理命令执行中的输入输出流
执行Linux命令时,必须及时读取进程的输入流(InputStream)和错误流(ErrorStream),否则可能导致进程阻塞,如果命令输出较大而未被读取,进程可能会等待缓冲区释放,从而造成死锁,建议使用单独的线程来读取输入流和错误流,或使用ProcessBuilder合并这两个流,以下是一个完整的示例:
ProcessBuilder pb = new ProcessBuilder("sh", "-c", "ls -l | grep txt");
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(line);
}
int exitCode = process.waitFor();
System.out.println("Exit Code: " + exitCode);
使用第三方库简化操作
对于更复杂的场景,可以使用第三方库如Apache Commons Exec,它提供了更高级的API和错误处理机制。

DefaultExecutor executor = new DefaultExecutor();
CommandLine cmdLine = new CommandLine("sh");
cmdLine.addArgument("-c");
cmdLine.addArgument("ls -l | grep txt");
int exitValue = executor.execute(cmdLine);
Commons Exec支持超时设置、流重定向和更灵活的参数处理,适合需要健壮性的企业级应用。
最佳实践与注意事项
- 命令安全性:避免直接拼接用户输入到命令中,以防命令注入攻击,建议使用参数化方式传递命令。
- 资源管理:确保在
finally块中关闭流和进程,避免资源泄漏。 - 超时处理:长时间运行的命令应设置超时,防止线程阻塞,使用
Process.waitFor(timeout, TimeUnit.SECONDS)。 - 跨平台兼容性:注意Windows和Linux命令的差异,必要时使用平台判断逻辑。
- 日志记录:记录命令的输入、输出和退出码,便于调试和问题排查。
通过合理选择执行方式并遵循最佳实践,可以高效、安全地在Java中实现Linux命令的执行,满足不同场景下的需求。



















