如何在Java ProcessBuilder中通过管道调用两个EXE程序?
解决Java ProcessBuilder实现raster2pgsql到psql的管道传输问题
你的问题核心在于:ProcessBuilder不会解析管道符|,这个符号是Windows cmd.exe(或Linux shell)的功能,不是进程本身的特性。你当前代码把|及后面的内容当成了raster2pgsql的表名参数,自然会出错。
下面提供两种可行的解决方案:
方案1:通过cmd.exe执行完整管道命令
直接调用Windows的cmd.exe,用/c参数让它执行包含管道的完整命令串。这种方式和你在命令行手动执行的逻辑完全一致:
ArrayList<String> cmd = new ArrayList<>(); cmd.add("cmd.exe"); cmd.add("/c"); // 用引号包裹完整命令,避免空格等特殊字符导致解析错误 cmd.add("raster2pgsql.exe -I -C -s 4326 -t 256x256 -l 4,16 C:\\raster\\sthlm.tif public.stockholm | psql -U postgresuser -d rasterdata -h localhost -p 5432"); ProcessBuilder b = MainDialog.gdalProcess; b.command(cmd); Process p = null; try { p = b.start(); } catch (IOException e) { e.printStackTrace(); } // 同时处理标准输出和错误输出,避免进程阻塞 try (InputStream processOutput = p.getInputStream(); InputStream processError = p.getErrorStream(); BufferedReader bufIn = new BufferedReader(new InputStreamReader(processOutput)); BufferedReader bufErr = new BufferedReader(new InputStreamReader(processError))) { String line; System.out.println("标准输出:"); while ((line = bufIn.readLine()) != null) { System.out.println(line); } System.out.println("\n错误输出:"); while ((line = bufErr.readLine()) != null) { System.out.println(line); } // 等待进程结束并获取退出码 int exitCode = p.waitFor(); System.out.println("\n进程退出码:" + exitCode); } catch (IOException | InterruptedException e) { e.printStackTrace(); }
方案2:手动连接两个进程的输入输出流
这种方式不依赖shell,完全通过Java代码处理两个进程的数据流,跨平台性更好(Linux/macOS也能复用逻辑)。核心是把raster2pgsql的标准输出流,直接写入psql的标准输入流:
// 配置raster2pgsql命令 ProcessBuilder rasterBuilder = new ProcessBuilder( "raster2pgsql.exe", "-I", "-C", "-s", "4326", "-t", "256x256", "-l", "4,16", "C:\\raster\\sthlm.tif", "public.stockholm" ); // 配置psql命令 ProcessBuilder psqlBuilder = new ProcessBuilder( "psql.exe", "-U", "postgresuser", "-d", "rasterdata", "-h", "localhost", "-p", "5432" ); try { // 启动两个进程 Process rasterProcess = rasterBuilder.start(); Process psqlProcess = psqlBuilder.start(); // 用线程处理数据流,避免主线程阻塞 new Thread(() -> { try (InputStream rasterOut = rasterProcess.getInputStream(); OutputStream psqlIn = psqlProcess.getOutputStream()) { byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = rasterOut.read(buffer)) != -1) { psqlIn.write(buffer, 0, bytesRead); } // 关闭输入流,告知psql数据传输完成 psqlIn.flush(); psqlIn.close(); } catch (IOException e) { e.printStackTrace(); } }).start(); // 读取psql的输出和错误信息 try (InputStream psqlOut = psqlProcess.getInputStream(); InputStream psqlErr = psqlProcess.getErrorStream(); BufferedReader bufOut = new BufferedReader(new InputStreamReader(psqlOut)); BufferedReader bufErr = new BufferedReader(new InputStreamReader(psqlErr))) { System.out.println("psql标准输出:"); String line; while ((line = bufOut.readLine()) != null) { System.out.println(line); } System.out.println("\npsql错误输出:"); while ((line = bufErr.readLine()) != null) { System.out.println(line); } // 等待两个进程执行完毕 int rasterExit = rasterProcess.waitFor(); int psqlExit = psqlProcess.waitFor(); System.out.println("\nraster2pgsql退出码:" + rasterExit); System.out.println("psql退出码:" + psqlExit); } catch (IOException | InterruptedException e) { e.printStackTrace(); } } catch (IOException e) { e.printStackTrace(); }
关键注意事项
- 处理进程输出时,必须同时读取标准输出和错误输出,否则当输出缓冲区满时,进程会陷入阻塞无法继续执行。
- 方案2中使用单独线程处理数据流,避免主线程被IO操作阻塞。
内容的提问来源于stack exchange,提问作者Paul
相关产品推荐
相关产品推荐

