如何通过Java Process获取Shell脚本的交互提示信息
如何获取Java Process执行交互式Shell脚本的输入提示
使用java.lang.Process执行需要用户交互式输入的Shell脚本时,无法获取到脚本中请求输入的提示信息。例如执行以下bash脚本:
read -p 'Enter your name: ' name; echo "Hello, $name!"
希望获取到**"Enter your name: "**这个提示,但现有代码只能获取到echo的输出,即仅打印outputFuture: Hello, myName!。
问题原因
- 流输出方向错误:
read -p的提示信息默认输出到标准错误流(stderr),而非标准输出流(stdout),原代码仅读取了stdout,因此无法捕获提示。 - 缓冲机制影响:bash通过
-c参数执行命令时默认处于非交互式模式,此时会启用全缓冲模式,提示信息会被缓存,不会实时输出,导致Java端无法及时读取。
解决方案
方案1:强制行缓冲并读取stderr
使用stdbuf命令强制stderr和stdout采用行缓冲,确保提示信息实时输出,同时在代码中读取stderr流获取提示。
修改后的Java代码:
import java.io.*; import java.util.concurrent.*; import java.util.logging.Logger; public class InteractiveProcess { private static final Logger LOGGER = Logger.getLogger(InteractiveProcess.class.getName()); public static void main(String[] args) throws IOException, InterruptedException, ExecutionException { // 使用stdbuf强制行缓冲,确保提示实时输出 ProcessBuilder pb = new ProcessBuilder("/bin/bash", "-c", "stdbuf -e L -o L read -p 'Enter your name: ' name; echo \"Hello, $name!\""); Process process = pb.start(); // 线程池处理双流读取,避免阻塞 ExecutorService executor = Executors.newFixedThreadPool(2); // 读取标准输出(echo内容) Future<String> outputFuture = executor.submit(() -> { BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); StringBuilder sb = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { sb.append(line).append("\n"); } return sb.toString().trim(); }); // 读取标准错误(read的提示信息) Future<String> errorFuture = executor.submit(() -> { BufferedReader reader = new BufferedReader(new InputStreamReader(process.getErrorStream())); StringBuilder sb = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { sb.append(line); } return sb.toString(); }); // 超时等待提示信息出现 String prompt = errorFuture.get(5, TimeUnit.SECONDS); LOGGER.info("获取到提示: " + prompt); // 输入内容并关闭输出流,告知脚本输入结束 OutputStream os = process.getOutputStream(); os.write("myName\n".getBytes()); os.flush(); os.close(); // 等待脚本执行完成 int exitCode = process.waitFor(); LOGGER.info("脚本退出码: " + exitCode); // 打印脚本输出 String output = outputFuture.get(); LOGGER.info("脚本输出: " + output); executor.shutdown(); } }
方案2:启用交互式bash模式
通过bash -i启用交互式模式,交互式shell默认采用行缓冲,read -p的提示会正常输出到stderr。只需修改ProcessBuilder参数:
ProcessBuilder pb = new ProcessBuilder("/bin/bash", "-i", "-c", "read -p 'Enter your name: ' name; echo \"Hello, $name!\"");
后续读取流、输入内容的逻辑与方案1一致。
注意事项
- 必须在独立线程中读取stdout和stderr,避免主线程因流阻塞而挂起。
- 输入完成后关闭输出流,让脚本知晓输入结束,避免脚本一直等待。
- 使用超时等待提示信息,防止程序因未收到提示而无限阻塞。
内容的提问来源于stack exchange,提问作者Non TF
相关产品推荐
相关产品推荐

