如何在Java中启动Docker容器并在容器内执行多条命令
在Java中启动Docker容器并执行内部多条命令的解决方案
问题根源分析
你之前的代码存在两个核心问题:
- 使用了
docker exec -it的-t参数(分配伪终端),Java的Process很难正确处理这种交互式终端的输入输出; - 调用
process.waitFor()会阻塞到bash进程结束,此时再写入输出流已经无效,且后续命令实际并未进入容器内部。
方案1:一次性执行多条非交互式命令(推荐)
如果不需要持续交互,直接通过bash -c将多条命令打包成一个参数传递给docker exec,无需进入交互式bash。
import java.util.Arrays; import java.util.List; public class DockerExecExample { public static void main(String[] args) { try { // 定义要执行的多条命令,用&&连接(前一条成功才执行下一条),或用;连接(无论前一条是否成功都执行) String multiCommands = "ls && pwd && echo '容器内执行成功'"; List<String> command = Arrays.asList( "sudo", "docker", "exec", "sawtooth-shell-default", "bash", "-c", multiCommands // 注意:这里必须是单独的一个参数 ); ProcessBuilder pb = new ProcessBuilder(command); pb.inheritIO(); // 将进程的输入输出重定向到当前Java进程的控制台 Process process = pb.start(); int exitCode = process.waitFor(); if (exitCode != 0) { throw new RuntimeException("容器内命令执行失败,退出码:" + exitCode); } } catch (Exception e) { throw new RuntimeException(e); } } }
关键注意点:
- 必须去掉
-it参数,非交互式场景不需要伪终端; bash -c后面的命令串必须作为单独的一个参数传入ProcessBuilder,不能拆分,否则会被解析成多个无效命令。
方案2:交互式执行多条命令
如果需要持续输入命令(比如模拟人工操作终端),则需要单独处理输入输出流,避免阻塞。
import java.io.*; import java.util.Arrays; import java.util.List; public class DockerInteractiveExec { public static void main(String[] args) { try { List<String> command = Arrays.asList( "sudo", "docker", "exec", "-i", // 仅保留-i参数,允许标准输入交互,去掉-t避免伪终端问题 "sawtooth-shell-default", "bash" ); ProcessBuilder pb = new ProcessBuilder(command); pb.redirectErrorStream(true); // 将错误输出合并到标准输出,方便统一读取 Process process = pb.start(); // 启动独立线程读取容器的输出,避免因缓冲区满导致进程阻塞 new Thread(() -> { try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { String line; while ((line = reader.readLine()) != null) { System.out.println("容器输出:" + line); } } catch (IOException e) { e.printStackTrace(); } }).start(); // 向容器内的bash写入命令,每条命令后加换行符(bash需要换行触发执行) try (OutputStreamWriter writer = new OutputStreamWriter(process.getOutputStream())) { writer.write("ls\n"); writer.write("pwd\n"); writer.write("echo '交互式命令执行'\n"); writer.write("exit\n"); // 必须写入exit,让bash进程退出,否则waitFor会一直阻塞 writer.flush(); } int exitCode = process.waitFor(); if (exitCode != 0) { throw new RuntimeException("交互式命令执行失败,退出码:" + exitCode); } } catch (Exception e) { throw new RuntimeException(e); } } }
关键注意点:
- 使用
-i替代-it,避免伪终端带来的交互问题; - 必须单独启动线程读取输出流,否则进程会因输出缓冲区满而挂起;
- 每条命令末尾必须加
\n,bash只有收到换行才会执行命令; - 最后必须写入
exit命令,让bash进程正常退出,否则process.waitFor()会一直阻塞。
内容的提问来源于stack exchange,提问作者Hamilton Hardy
相关产品推荐
相关产品推荐

