java调用外部程序---- Runtime.getRuntime().exec

本文详细介绍了如何使用Java的Runtime.getRuntime().exec方法来调用外部程序和系统命令,包括处理DOS内部命令、打开不可执行文件、处理标准输出等问题,并分析了exec方法的不同重载形式及其参数含义,以及Process类的关键方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 Runtime.getRuntime().exec

用Java编写应用时,有时需要在程序中调用另一个现成的可执行程序或系统命令,这时可以通过组合使用Java提供的Runtime类和Process类的方法实现。下面是一种比较典型的程序模式:   
...   
Process   process   =   Runtime.getRuntime().exec( ".//p.exe ");   
process.waitfor(   );   
...   
在上面的程序中,第一行的“.//p.exe”是要执行的程序名,Runtime.getRuntime()返回当前应用程序的Runtime对象,该对象的exec()方法指示Java虚拟机创建一个子进程执行指定的可执行程序,并返回与该子进程对应的Process对象实例。通过Process可以控制该子进程的执行或获取该子进程的信息。第二条语句的目的等待子进程完成再往下执行。   
但在windows平台上,如果处理不当,有时并不能得到预期的结果。下面是笔者在实际编程中总结的几种需要注意的情况:   
1、执行DOS的内部命令   
如果要执行一条DOS内部命令,有两种方法。一种方法是把命令解释器包含在exec()的参数中。例如,执行dir命令,在NT上,   可写成exec( "cmd.exe   /c   dir "),在windows   95/98下,可写成“command.exe   /c   dir”,其中参数“/c”表示命令执行后关闭Dos立即关闭窗口。另一种方法是,把内部命令放在一个批命令my_dir.bat文件中,在Java程序中写成exec( "my_dir.bat ")。如果仅仅写成exec( "dir "),Java虚拟机则会报运行时错误。前一种方法要保证程序的可移植性,需要在程序中读取运行的操作系统平台,以调用不同的命令解释器。后一种方法则不需要做更多的处理。   
2、打开一个不可执行的文件   
打开一个不可执行的文件,但该文件存在关联的应用程序,则可以有两种方式。   以打开一个word文档a.doc文件为例,Java中可以有以下两种写法:   
exec( "start   .//a.doc ");   
exec( "   c://Program   Files//Microsoft   Office//office//winword.exe   .//a.doc ");   
显然,前一种方法更为简捷方便。   
3、执行一个有标准输出的DOS可执行程序   
在windows平台上,运行被调用程序的DOS窗口在程序执行完毕后往往并不会自动关闭,从而导致Java应用程序阻塞在waitfor(   )。导致该现象的一个可能的原因是,该可执行程序的标准输出比较多,而运行窗口的标准输出缓冲区不够大。解决的办法是,利用Java提供的Process类提供的方法让Java虚拟机截获被调用程序的DOS运行窗口的标准输出,在waitfor()命令之前读出窗口的标准输出缓冲区中的内容。一段典型的程序如下:   
...   
String   ls_1;   
Process   process   =   Runtime.getRuntime().exec( "cmd   /c   dir   //windows ");   
BufferedReader   bufferedReader   =   new   BufferedReader(   /   
new   InputStreamReader(process.getInputStream());   
while   (   (ls_1=bufferedReader.readLine())   !=   null)   
System.out.println(ls_1);   

process.waitfor(   );  

 

转:http://blog.youkuaiyun.com/legendmohenote/article/details/5828455

 

Runtime.getRuntime().exec()方法主要用于执行外部的程序或命令

Runtime.getRuntime().exec共有六个重载方法:

  • public Process exec(String command)

    在单独的进程中执行指定的字符串命令。

  • public Process exec(String [] cmdArray)

        在单独的进程中执行指定命令和变量

  • public Process exec(String command, String [] envp)

    在指定环境的独立进程中执行指定命令和变量

  • public Process exec(String [] cmdArray, String [] envp)

    在指定环境的独立进程中执行指定的命令和变量

  • public Process exec(String command,String[] envp,File dir)

    在有指定环境和工作目录的独立进程中执行指定的字符串命令

  • public Process exec(String[] cmdarray,String[] envp,File dir)

    在指定环境和工作目录的独立进程中执行指定的命令和变量

我们先来比较exec(String command)与exec(String[] cmdArray)的区别,其实他们是等价的,最终都会调用:

exec(String[] cmdarray,String[] envp,File dir),我们看看方法exec(String cmdarray,String[] envp,File dir) throws IOException的实现代码:

?
1
2
3
4
5
6
7
8
public  Process exec(String command, String[] envp, File dir)  throws  IOException {
     if  (command.length() ==  0 throw  new  IllegalArgumentException( "Empty command" );
     StringTokenizer st =  new  StringTokenizer(command);
     String[] cmdarray =  new  String[st.countTokens()];
     for  ( int  i =  0 ; st.hasMoreTokens(); i++)
         cmdarray[i] = st.nextToken();
     return  exec(cmdarray, envp, dir);
}

从上面的代码,我们可以看出最终调用的代码都是:exec(String[] cmdArray,String envp,File  dir)。exec(String command)相当于exec(command,null,null),exec(String[] cmdArray)相当于exec(cmdArray,null,null)。

参数说明

cmdarray - 包含所调用命令及其参数的数组。

envp - 字符串数组,其中每个元素的环境变量的设置格式为 name=value,如果子进程应该继承当前进程的环境,或该参数为 null。

dir - 子进程的工作目录;如果子进程应该继承当前进程的工作目录,则该参数为 null。

 

另外,执行exec(String command)不等同于直接执行command line命令,比如命令:

?
1
javap -l xxx > output.txt

这时要用exec(String[] cmdArray)。如例:

?
1
2
Process p = Runtime.getRuntime().exec( new  String[]{ "/bin/sh" , "-c" ,
     "javap -l xxx > output.txt" });

 

关于返回结果类型:Process,它有几个方法:

1.destroy():杀掉子进程

2.exitValue():返回子进程的出口值,值 0 表示正常终止

3.getErrorStream():获取子进程的错误流

4.getInputStream():获取子进程的输入流

5.getOutputStream():获取子进程的输出流

6.waitFor():导致当前线程等待,如有必要,一直要等到由该 Process 对象表示的进程已经终止。如果已终止该子进程,此方法立即返回。如果没有终止该子进程,调用的线程将被阻塞,直到退出子进程,根据惯例,0 表示正常终止

 

转:http://my.oschina.net/fhd/blog/318723

 

<think>我们正在处理一个关于在Linux环境下使用JavaRuntime.getRuntime().exec方法调用Python脚本失败的问题。根据用户提供的信息和引用内容,我们可以总结出一些常见问题和解决方法。 首先,用户的问题描述是:在Linux环境下,使用JavaRuntime.getRuntime().exec执行Python脚本失败,需要故障排除。 根据引用内容,我们可以归纳出以下几个常见问题及解决方案: 1. **路径问题**:Python脚本的路径或外部文件路径不正确,导致程序找不到文件- 解决方案:在`exec`方法中指定工作目录(使用`File`参数),如引用[3]所示:`Runtime.getRuntime().exec(cmd, null, new File(path));`,其中`path`为Python脚本所在目录。 2. **命令参数问题**:如果命令比较复杂(比如包含管道、重定向等),直接使用字符串可能会导致解析错误。 - 解决方案:对于复杂的命令,使用字符串数组来传递命令和参数,或者使用`sh -c`来执行整个命令字符串(如引用[5]所示)。 - 例如:`Runtime.getRuntime().exec(new String[]{"sh", "-c", "your_command_here"});` 3. **阻塞问题**:`exec`方法启动的进程可能会因为输出缓冲区满而导致Java进程阻塞(等待缓冲区被读取)。 - 解决方案:读取进程的输出流和错误流,避免缓冲区满。可以使用两个线程分别读取标准输出和标准错误流(如引用[2]中提到的问题)。 4. **权限问题**:Python脚本可能没有执行权限。 - 解决方案:确保Python脚本具有可执行权限(使用`chmod +x script.py`)。 5. **环境变量问题**:Java进程的环境变量可能不包含Python解释器的路径。 - 解决方案:在命令中指定Python解释器的绝对路径(如`/usr/bin/python3`),或者在执行命令前设置环境变量。 6. **等待进程结束**:如果需要等待Python脚本执行完成,应该调用`process.waitFor()`(如引用[4]所示)。 7. **错误处理**:获取进程的错误输出,以便了解失败原因。 下面,我们将提供一个结构化的故障排除步骤,并给出一个示例代码。 ### 故障排除步骤 1. **检查Python脚本路径和工作目录**: - 确保传递给`exec`的Python脚本路径正确,并且如果脚本依赖其他文件,应设置工作目录。 2. **检查命令格式**: - 对于简单命令,可以直接使用字符串;对于复杂命令(包含空格、引号、管道等),使用字符串数组或通过`sh -c`执行。 3. **处理输入/输出流**: - 创建线程读取进程的输入流和错误流,避免进程阻塞。 4. **检查权限**: - 确保Python脚本有可执行权限,并且Java进程有权限执行该脚本。 5. **检查环境变量**: - 可以在Java中打印环境变量,确认PATH是否包含Python解释器路径,或者直接使用Python解释器的绝对路径。 6. **错误输出**: - 捕获并打印进程的错误输出,这通常是解决问题的关键。 ### 示例代码 以下是一个示例代码,展示了如何安全地调用Python脚本: ```java import java.io.*; public class CallPython { public static void main(String[] args) { // 使用绝对路径指定Python解释器,以及脚本的绝对路径 String pythonScript = "/path/to/your/script.py"; String[] cmd = {"/usr/bin/python3", pythonScript}; try { // 启动进程 Process process = Runtime.getRuntime().exec(cmd, null, new File("/path/to/script/directory")); // 读取标准输出流 BufferedReader stdoutReader = new BufferedReader(new InputStreamReader(process.getInputStream())); // 读取标准错误流 BufferedReader stderrReader = new BufferedReader(new InputStreamReader(process.getErrorStream())); // 启动两个线程分别读取输出和错误,避免阻塞 Thread stdoutThread = new Thread(() -> { String line; try { while ((line = stdoutReader.readLine()) != null) { System.out.println("STDOUT: " + line); } } catch (IOException e) { e.printStackTrace(); } }); Thread stderrThread = new Thread(() -> { String line; try { while ((line = stderrReader.readLine()) != null) { System.err.println("STDERR: " + line); } } catch (IOException e) { e.printStackTrace(); } }); stdoutThread.start(); stderrThread.start(); // 等待进程结束 int exitCode = process.waitFor(); System.out.println("Exit code: " + exitCode); // 等待线程结束 stdoutThread.join(); stderrThread.join(); } catch (Exception e) { e.printStackTrace(); } } } ``` ### 注意事项 - 如果Python脚本需要参数,可以在`cmd`数组后面添加,例如:`String[] cmd = {"/usr/bin/python3", pythonScript, "arg1", "arg2"};`。 - 如果遇到特殊字符,使用字符串数组可以避免shell解析问题。 - 使用`sh -c`的方式可以执行复杂的shell命令,但要注意转义。 ### 相关问题 1. 如何避免Java中`Runtime.getRuntime().exec`执行外部命令时的阻塞问题? 2.Java调用外部脚本时,如何传递参数并处理参数中的空格和特殊字符? 3. 如何捕获并实时显示外部进程的输出流和错误流? 4. 在Linux环境下,如何为Java进程设置环境变量? 5. 如何确保Java进程有足够的权限执行外部脚本或命令? 希望这些信息能帮助您解决问题。如果问题仍然存在,请检查具体的错误输出以进一步诊断。</think>### 在Linux环境下Java调用Python脚本的故障排除 当使用`Runtime.getRuntime().exec()`在Linux环境中调用Python脚本失败时,常见问题及解决方法如下: #### 1. **路径与环境变量问题** - **问题描述**:Java进程未正确识别Python解释器路径或脚本路径。 - **解决方案**: - 使用Python绝对路径:`/usr/bin/python3`(通过`which python3`确认路径) - 指定脚本绝对路径:`new File("/path/to/script.py")` - 显式设置工作目录(关键): ```java Process p = Runtime.getRuntime().exec( new String[]{"python3", "script.py"}, null, // 环境变量(null表示继承当前环境) new File("/path/to/script/directory") // 工作目录 ); ``` 引用[3]验证了工作目录设置的重要性[^3]。 #### 2. **命令参数格式错误** - **问题描述**:特殊字符(空格、引号等)导致命令解析失败。 - **解决方案**: - 使用字符串数组而非单字符串: ```java // 错误示例:exec("python3 /path/with space/script.py") // 正确示例: String[] cmd = { "/usr/bin/python3", "/path/with space/script.py", "arg1", "arg2" }; Runtime.getRuntime().exec(cmd); ``` - 复杂命令通过`sh -c`执行(如含管道、重定向): ```java String[] cmd = {"sh", "-c", "python3 script.py | grep 'error'"}; Runtime.getRuntime().exec(cmd); ``` 引用[5]展示了复杂命令的处理方式[^5]。 #### 3. **流阻塞导致进程挂起** - **问题描述**:未消费输出流导致缓冲区满,进程阻塞。 - **解决方案**:异步读取输出流和错误流: ```java Process p = Runtime.getRuntime().exec(cmd); // 启动线程读取输出流 new Thread(() -> { try (BufferedReader reader = new BufferedReader( new InputStreamReader(p.getInputStream()))) { String line; while ((line = reader.readLine()) != null) { System.out.println("OUT: " + line); } } catch (IOException e) { /* 处理异常 */ } }).start(); // 同样方式处理错误流(p.getErrorStream()) p.waitFor(); // 等待进程结束 ``` 引用[2]强调了流处理的重要性[^2]。 #### 4. **权限与执行环境问题** - **检查项**: - Python脚本是否可执行:`chmod +x script.py` - Java进程用户是否有脚本读写权限 - Python虚拟环境激活:通过绝对路径调用虚拟环境中的Python - 关键依赖缺失:在Python脚本开头添加`import sys; print(sys.path)`检查模块路径 #### 5. **超时与进程管理** - 设置执行超时: ```java if (!p.waitFor(30, TimeUnit.SECONDS)) { p.destroy(); // 强制终止进程 throw new TimeoutException("Python script timed out"); } ``` #### 完整示例代码 ```java try { String[] cmd = { "/usr/bin/python3", "/data/scripts/process_data.py", "input.csv", "output.csv" }; Process p = Runtime.getRuntime().exec( cmd, null, new File("/data/scripts") // 工作目录 ); // 异步读取流 consumeStream(p.getInputStream(), "OUT"); consumeStream(p.getErrorStream(), "ERR"); int exitCode = p.waitFor(); System.out.println("Exit code: " + exitCode); } catch (Exception e) { e.printStackTrace(); } // 流消费方法 void consumeStream(InputStream is, String type) { new Thread(() -> { try (BufferedReader br = new BufferedReader(new InputStreamReader(is))) { String line; while ((line = br.readLine()) != null) { System.out.println(type + ": " + line); } } catch (IOException e) { /* 处理异常 */ } }).start(); } ``` ### 常见错误排查表 | 现象 | 可能原因 | 验证方法 | |-----------------------|--------------------------|-----------------------------| | 报`No such file` | 路径错误/未设置工作目录 | 使用`ls -l`验证文件是否存在 | | 脚本执行但无输出 | 输出流阻塞 | 添加流消费线程 | | 报`Permission denied` | 缺少执行权限 | `chmod +x script.py` | | 模块导入失败 | Python环境路径错误 | 在脚本中打印`sys.path` |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值