在Java中执行CMD命令是一项常见的需求,特别是在需要与操作系统交互、调用外部工具或自动化任务时,Java提供了多种方式来实现这一功能,其中最常用的是通过Runtime
类和ProcessBuilder
类,下面将详细介绍这两种方法的使用场景、代码示例、注意事项以及最佳实践。

Runtime
类是Java中用于与运行时环境交互的类,它提供了一个exec
方法来执行外部命令。Runtime
类是一个单例类,通过getRuntime()
方法获取实例,执行一个简单的CMD命令如dir
(列出当前目录文件)可以使用以下代码:
try { Process process = Runtime.getRuntime().exec("dir"); 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 (IOException | InterruptedException e) { e.printStackTrace(); }
上述代码中,exec
方法启动一个进程,InputStreamReader
和BufferedReader
用于读取命令的输出流,waitFor
方法等待进程执行完毕并获取退出码,需要注意的是,exec
方法在处理复杂命令(如包含空格或特殊字符的命令)时可能会出现问题,此时需要将命令拆分为字符串数组,执行cmd /c dir
可以这样写:
String[] command = {"cmd", "/c", "dir"}; Process process = Runtime.getRuntime().exec(command);
Runtime.exec
方法在处理命令参数、环境变量和工作目录时不够灵活,因此Java推荐使用ProcessBuilder
类。ProcessBuilder
类提供了更强大的功能,可以方便地设置命令参数、工作目录和环境变量,以下是一个使用ProcessBuilder
执行dir
命令的示例:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; public class CmdExample { public static void main(String[] args) { ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "dir"); pb.directory(new File("C:\\")); // 设置工作目录 try { 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); } catch (IOException | InterruptedException e) { e.printStackTrace(); } } }
在上述代码中,ProcessBuilder
的构造函数接受一个字符串数组作为命令和参数,directory
方法用于设置进程的工作目录,与Runtime.exec
相比,ProcessBuilder
在处理复杂命令时更加直观和可靠。

执行CMD命令时,需要注意以下几点:命令的输出流(InputStream
)和错误流(ErrorStream
)需要及时读取,否则可能会导致进程阻塞,如果输出流缓冲区满,进程可能会等待读取而无法继续执行,对于长时间运行的命令,建议使用单独的线程读取输出流和错误流,以避免主线程阻塞,以下是一个多线程读取输出的示例:
import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; public class CmdWithThreads { public static void main(String[] args) { ProcessBuilder pb = new ProcessBuilder("ping", "www.baidu.com"); try { Process process = pb.start(); Thread outputThread = new Thread(() -> { try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { String line; while ((line = reader.readLine()) != null) { System.out.println("[OUTPUT] " + line); } } catch (IOException e) { e.printStackTrace(); } }); Thread errorThread = new Thread(() -> { try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream()))) { String line; while ((line = reader.readLine()) != null) { System.out.println("[ERROR] " + line); } } catch (IOException e) { e.printStackTrace(); } }); outputThread.start(); errorThread.start(); outputThread.join(); errorThread.join(); int exitCode = process.waitFor(); System.out.println("Exit Code: " + exitCode); } catch (IOException | InterruptedException e) { e.printStackTrace(); } } }
在上述代码中,两个分别用于读取输出流和错误流的线程被启动,并使用join
方法等待线程执行完毕,这样可以有效避免进程阻塞问题。
执行CMD命令时还需要注意安全性问题,如果命令参数来自用户输入,需要进行严格的验证和过滤,以避免命令注入攻击,命令注入是指攻击者通过恶意输入修改原本要执行的命令,从而执行未授权的操作,以下是一个不安全的示例:
String userInput = "malicious && rm -rf /"; ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "echo " + userInput);
上述代码中,如果userInput
包含恶意命令,可能会导致系统受损,为了避免这种情况,应避免直接拼接用户输入到命令中,而是使用参数化方式传递输入。

ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "echo"); pb.command().add(userInput);
这种方式下,userInput
会被作为参数传递,而不是直接拼接到命令中,从而减少命令注入的风险。
以下是一个表格总结了Runtime
和ProcessBuilder
的区别:
特性 | Runtime.exec() | ProcessBuilder |
---|---|---|
灵活性 | 较低,仅支持简单命令 | 较高,支持复杂命令和环境变量设置 |
工作目录设置 | 不支持 | 支持,通过directory 方法 |
环境变量设置 | 不支持 | 支持,通过environment 方法 |
命令参数处理 | 需手动拆分字符串数组 | 直接支持字符串数组 |
输出流读取 | 需手动处理 | 需手动处理,但支持多线程读取 |
推荐使用场景 | 简单命令执行 | 复杂命令、环境配置、长时间运行任务 |
相关问答FAQs:
-
问:为什么使用
ProcessBuilder
时,进程的输出流和错误流需要及时读取?
答:如果不及时读取输出流和错误流,缓冲区可能会被填满,导致进程无法继续输出,从而阻塞进程,如果命令输出大量数据,而主线程没有及时读取,进程可能会等待缓冲区释放空间,最终导致程序无响应,建议使用单独的线程读取输出流和错误流,或者定期读取流中的数据。 -
问:如何在Java中执行需要管理员权限的CMD命令?
答:执行需要管理员权限的CMD命令时,需要确保Java进程本身具有足够的权限,在Windows系统中,可以通过以下方式实现:- 以管理员身份运行Java应用程序(右键点击IDE或JAR文件,选择“以管理员身份运行”)。
- 在命令中使用
runas
工具,ProcessBuilder pb = new ProcessBuilder("cmd", "/c", "runas /user:Administrator \"echo admin command\"");
注意:
runas
会提示输入密码,因此在自动化场景中可能需要结合其他工具(如PsExec
)来实现无权限提升,不同操作系统的权限管理方式不同,需根据具体环境调整方案。