Runtime.exec不能直接执行带管道、重定向等shell语法的命令,因其不经过shell解析;正确做法是显式调用/bin/sh -c(Linux)或cmd /c(Windows)并传入命令字符串,同时必须主动读取inputStream和errorStream以防阻塞。

Runtime.exec 不能直接执行带管道、重定向或多个命令的 shell 字符串,必须显式调用 shell 并传入 -c 参数;否则常见命令如 ls | grep java 或 cd /tmp && pwd 会静默失败或抛出 IOException。
为什么 Runtime.exec("ls -l | grep java") 总是没输出?
因为 Runtime.exec 不经过 shell 解析,|、&&、> 这些符号被当成普通参数传给 ls,导致命令根本无法识别。JVM 启动的是纯进程,不是 shell 子进程。
- 正确做法是显式调用
/bin/sh(Linux/macOS)或cmd.exe(Windows),并用-c(或/c)让 shell 解释后续字符串 - Linux 示例:
Runtime.exec(new String[]{"/bin/sh", "-c", "ls -l | grep java"}) - Windows 示例:
Runtime.exec(new String[]{"cmd.exe", "/c", "dir | findstr "java""}) - 避免拼接字符串:用
String[]形式传参,防止空格、引号引发的解析错乱
如何可靠读取命令的标准输出和错误流?
不主动消费 Process.getInputStream() 和 Process.getErrorStream(),会导致子进程阻塞(尤其输出量大时),waitFor() 永远不返回。
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
- 必须分别启动线程或使用
InputStreamReader+BufferedReader实时读取两个流,否则可能死锁 - 推荐封装工具方法,例如用
IOUtils.copy(inputStream, outputStream)(Apache Commons)或手动循环read() - 别只读
stdout:很多命令把错误写到stderr(比如gcc编译失败),忽略它等于丢掉关键信息 - 示例片段:
Process p = Runtime.exec(cmdArray); new Thread(() -> copy(p.getInputStream(), System.out)).start(); new Thread(() -> copy(p.getErrorStream(), System.err)).start();
为什么在 Windows 上执行 dir 成功,但 mvn clean 找不到命令?
因为 Runtime.exec 启动的进程不继承父进程的完整环境变量(尤其是 PATH),而 mvn 是脚本或批处理文件,依赖 PATH 定位。
- 解决方案一:用绝对路径调用可执行文件,如
"C:\Program Files\Maven\bin\mvn.cmd" - 解决方案二:显式构造
ProcessBuilder并设置环境变量:pb.environment().put("PATH", "...") - 注意
.cmd和.bat在 Windows 上必须显式指定后缀,Runtime.exec("mvn")默认不匹配任何扩展名 - Linux 下同理:
git可能不在/usr/bin,需查which git或设PATH
真正麻烦的从来不是“怎么调”,而是“怎么确保它在所有环境里稳定输出、不卡住、不错读、不漏错”。别省略流处理,别信默认 PATH,也别把复杂 shell 逻辑硬塞进 exec —— 该写脚本就写脚本,Java 负责调用和收尾。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










