processbuilder比runtime.exec更安全可控,因其将命令拆分为字符串数组避免shell解析问题,支持工作目录、环境变量等配置,且默认不调用shell;而runtime.exec(string)易受命令注入和参数分割影响,必须避免拼接字符串。

ProcessBuilder比Runtime.exec更可控,尤其在参数传入和环境隔离上
Runtime.exec直接拼接字符串调用命令,容易因空格、引号、特殊字符导致执行失败或命令注入;ProcessBuilder把命令拆成String数组,天然规避shell解析歧义。比如执行ls -l /tmp/with space,用Runtime.exec("ls -l /tmp/with space")会报No such file or directory,而new ProcessBuilder("ls", "-l", "/tmp/with space")能正确识别路径。
- ProcessBuilder默认不走shell,
Runtime.exec(String)底层仍依赖系统shell(如/bin/sh),带来额外攻击面 - ProcessBuilder支持链式配置:
directory()设工作目录、environment().put()改环境变量、inheritIO()复用当前进程IO流 - Runtime.exec的
exec(String[])重载虽也支持数组,但无法设置工作目录或环境变量,必须靠ProcessBuilder补足
别用Runtime.exec(String)传含空格或通配符的路径
这是最常踩的坑:把用户输入或动态路径直接塞进Runtime.exec(String),结果被shell当多个参数切分。例如Runtime.exec("ffmpeg -i " + userInputPath),如果userInputPath是/data/video 1.mp4,实际执行的是ffmpeg -i /data/video和1.mp4两个参数,报错Invalid argument或静默失败。
- 绝对不要拼接命令字符串——哪怕你加了引号,不同系统shell行为不一致(bash vs dash vs Windows cmd)
- 必须用
Runtime.exec(String[])或直接上ProcessBuilder,把每个参数作为独立String元素 - 如果真要走shell(比如需要管道
|或重定向>),显式调用sh -c并严格控制参数个数:new ProcessBuilder("sh", "-c", "cmd1 | cmd2", "_", arg1, arg2),其中"_"占位符让$1对应arg1
ProcessBuilder.start()后必须处理输入输出流,否则可能死锁
子进程的stdout或stderr缓冲区满时会阻塞,而Java主线程若没及时读取,整个进程就卡住——现象是process.waitFor()永远不返回,CPU空转,日志也没输出。
- 不能只调
process.getInputStream()却不读;也不能只读getInputStream()却忽略getErrorStream(),stderr满也会卡住 - 简单场景用
process.inheritIO()把子进程IO直接连到JVM控制台(适合调试或脚本式调用) - 生产环境建议用线程分别读取两个流,或用
CompletableFuture异步消费,避免阻塞主线程 - 注意:
ProcessBuilder.redirectErrorStream(true)可合并stderr到stdout,减少线程数,但会丢失错误来源标识
Windows下要注意路径分隔符和可执行文件后缀
ProcessBuilder在Windows上不会自动补.exe,也不理解/路径分隔符语义。比如new ProcessBuilder("ping", "8.8.8.8")在Linux能跑,在Windows可能报Cannot run program "ping": CreateProcess error=2,因为没找到ping.exe。
立即学习“Java免费学习笔记(深入)”;
- 显式写全名:
new ProcessBuilder("ping.exe", "8.8.8.8"),或用System.getProperty("os.name")做平台判断 - 路径统一用
File.separator或Paths.get()构造,避免硬写"C:\temp\file.txt"或"C:/temp/file.txt" - Windows的
cmd.exe /c方式虽然兼容旧习惯,但引入额外进程层,错误码、信号传递都变复杂,非必要不推荐
真正麻烦的从来不是启动进程,而是等它结束时不知道它卡在哪——IO没读、路径没对、权限没给、环境变量冲突,这些点串起来就是一整条排查链。别省那几行流处理代码。










