使用Runtime.getRuntime().exec()方法的几个陷阱 (转)

Runtime 封装着java程序的运行时环境。通过Runtime实例,java应用能够与其运行的环境连接。Runtime在jvm中保持一个单例,所以不能通过Runtime类的构造函数。只能通过Runtime.getRuntime()来获的当前Runtime的一个实例。获得Runtime实例后,就可以通过Runtime的exec()方法在当前jvm进程外启动其他进程了。很常见的一个应用就是,启动浏览器进程来显示一个程序的帮助页面。

在Runtime类中存在四个exec()重载方法.
Java代码 收藏代码

public Process exec(String command);  
public Process exec(String [] cmdArray);  
public Process exec(String command, String [] envp);  
public Process exec(String [] cmdArray, String [] envp);  

主要参数是要启动进程的名称,以及启动该进程时需要的参数。然后是一些环境相关的属性。envp是已name=value,
形式传入的。具体查看下源码便一目了然了。
通常,启动另外一个进程后,需要获取另外一个进程的执行结果,然后根据结果执行后续的流程。要获取外部进程的运行结果,可以调用Process的exitValue() 方法。下面代码中启动一个java编译器进程。

Java代码

try {  
    Runtime rt = Runtime.getRuntime();  
    Process proc = rt.exec("javac");  
    int exitVal = proc.exitValue();  
    System.out.println("Process exitValue: " + exitVal);  
} catch (Throwable t) {  
    t.printStackTrace();  
}  

不幸的是,你将看到下面的结果:

java.lang.IllegalThreadStateException: process has not exited 
at java.lang.ProcessImpl.exitValue(Native Method) 
at com.test.runtime.Test.BadExecJavac(Test.java:13) 
at com.test.runtime.Test.main(Test.java:5) 

原因是exitValue()方法并不会等待外部进程结束。如果外部进程还未结束,exitValue()将会抛出IllegalThreadStateException。解决办法就是调用Process的waitfor()方法。waitfor()方法会挂起当前线程,一直等到外部进程结束。当然使用exitValue()或者waitfor()完全取决你的需求。可以设个boolean标志,来确定使用哪个。运行下面的代码:
Java代码

try {  
    Runtime rt = Runtime.getRuntime();  
    Process proc = rt.exec("javac");  
    int exitVal = proc.waitFor();  
    System.out.println("Process exitValue: " + exitVal);  
} catch (Throwable t) {  
    t.printStackTrace();  
}  

发现程序被阻塞了,什么原因呢?JDK’s Javadoc文档解释说:
Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, and even deadlock.
翻译:
一些平台只为标准输入输出提供有限的缓存。错误的写子进程的输入流或者错误的都子进程的输出流都有可能造成子进程的阻塞,甚至是死锁。

解决上面问题的办法就是程序中将子进程的输出流和错误流都输出到标准输出中。
Java代码

try {  
    Runtime rt = Runtime.getRuntime();  
    Process proc = rt.exec("javac");  
    InputStream stderr = proc.getErrorStream();  
    InputStreamReader isr = new InputStreamReader(stderr);  
    BufferedReader br = new BufferedReader(isr);  
    String line = null;  
    System.out.println("<ERROR>");  
    while ((line = br.readLine()) != null)  
        System.out.println(line);  
    System.out.println("</ERROR>");  
    int exitVal = proc.waitFor();  
    System.out.println("Process exitValue: " + exitVal);  
} catch (Throwable t) {  
    t.printStackTrace();  
}  

上面的代码中仅仅是输出了错误流,并没有输出子进程的输出流。在程序中最好是能将子进程的错误流和输出流都能输出并清空。

在windows系统中,很多人会利用Runtime.exec()来调用不可执行的命令。例如dir和copy;
Java代码

try {  
    Runtime rt = Runtime.getRuntime();  
    Process proc = rt.exec("dir");  
    InputStream stdin = proc.getInputStream();  
    InputStreamReader isr = new InputStreamReader(stdin);  
    BufferedReader br = new BufferedReader(isr);  
    String line = null;  
    System.out.println("<OUTPUT>");  
    while ((line = br.readLine()) != null)  
        System.out.println(line);  
    System.out.println("</OUTPUT>");  
    int exitVal = proc.waitFor();  
    System.out.println("Process exitValue: " + exitVal);  
} catch (Throwable t) {  
    t.printStackTrace();  
}  

运行上面的代码,将会得到一个错误代码为2的错误。在win32系统中,error=2表示文件未找到。也就是不存在dir.exe和copy.exe。这是因为dir命令式windows中命令行解析器的一部分,并不是单独的一个可执行的命令。要运行上面的命令,得先启动windows下的命令行解析器command.com或者cmd.exe,这个取决于windows的系统的版本。
Java代码

import java.io.BufferedReader;  
import java.io.IOException;  
import java.io.InputStream;  
import java.io.InputStreamReader;  

class StreamGobbler extends Thread {  
    InputStream is;  
    String type;  

    StreamGobbler(InputStream is, String type) {  
        this.is = is;  
        this.type = type;  
    }  

    public void run() {  
        try {  
            InputStreamReader isr = new InputStreamReader(is);  
            BufferedReader br = new BufferedReader(isr);  
            String line = null;  
            while ((line = br.readLine()) != null)  
                System.out.println(type + ">" + line);  
        } catch (IOException ioe) {  
            ioe.printStackTrace();  
        }  
    }  
}  

public class GoodWindowsExec {  
    public static void main(String args[]) {  
        if (args.length < 1) {  
            System.out.println("USAGE: java GoodWindowsExec <cmd>");  
            System.exit(1);  
        }  

        try {  
            String osName = System.getProperty("os.name");  
            String[] cmd = new String[3];  
            if (osName.equals("Windows NT")) {  
                cmd[0] = "cmd.exe";  
                cmd[1] = "/C";  
                cmd[2] = args[0];  
            } else if (osName.equals("Windows 95")) {  
                cmd[0] = "command.com";  
                cmd[1] = "/C";  
                cmd[2] = args[0];  
            }  

            Runtime rt = Runtime.getRuntime();  
            System.out.println("Execing " + cmd[0] + " " + cmd[1] + " " + cmd[2]);  
            Process proc = rt.exec(cmd);  

            StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(), "ERROR");  

            StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream(), "OUTPUT");  

            errorGobbler.start();  
            outputGobbler.start();  

            int exitVal = proc.waitFor();  
            System.out.println("ExitValue: " + exitVal);  
        } catch (Throwable t) {  
            t.printStackTrace();  
        }  
    }  
}  

另外,Runtime.exec()并不是命令解析器,这是启动某个进程。并不能执行一些命令行的命令。下面是一个常见的错误:

try {  
    Runtime rt = Runtime.getRuntime();  
    Process proc = rt.exec("java jecho 'Hello World' > test.txt");  

    StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(), "ERROR");  

    StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream(), "OUTPUT");  

    errorGobbler.start();  
    outputGobbler.start();  

    int exitVal = proc.waitFor();  
    System.out.println("ExitValue: " + exitVal);  
} catch (Throwable t) {  
    t.printStackTrace();  
}  

上面的代码希望像DOS系统中一样将命令的执行结果输出到文件中去。但是Runtime.exec()并不是命令行解析器。要想重定向输出流,必须在程序中编码实现。
Java代码

import java.util.*;  
import java.io.*;  

class StreamGobbler extends Thread {  
    InputStream is;  
    String type;  
    OutputStream os;  

    StreamGobbler(InputStream is, String type) {  
        this(is, type, null);  
    }  

    StreamGobbler(InputStream is, String type, OutputStream redirect) {  
        this.is = is;  
        this.type = type;  
        this.os = redirect;  
    }  

    public void run() {  
        try {  
            PrintWriter pw = null;  
            if (os != null)  
                pw = new PrintWriter(os);  
            InputStreamReader isr = new InputStreamReader(is);  
            BufferedReader br = new BufferedReader(isr);  
            String line = null;  
            while ((line = br.readLine()) != null) {  
                if (pw != null)  
                    pw.println(line);  
                System.out.println(type + ">" + line);  
            }  
            if (pw != null)  
                pw.flush();  
        } catch (IOException ioe) {  
            ioe.printStackTrace();  
        }  
    }  
}  

public class GoodWinRedirect {  
    public static void main(String args[]) {  
        if (args.length < 1) {  
            System.out.println("USAGE java GoodWinRedirect <outputfile>");  
            System.exit(1);  
        }  

        try {  
            FileOutputStream fos = new FileOutputStream(args[0]);  
            Runtime rt = Runtime.getRuntime();  
            Process proc = rt.exec("java jecho 'Hello World'");  

            StreamGobbler errorGobbler = new StreamGobbler(proc.getErrorStream(), "ERROR");  
            StreamGobbler outputGobbler = new StreamGobbler(proc.getInputStream(), "OUTPUT", fos);  

            errorGobbler.start();  
            outputGobbler.start();  

            int exitVal = proc.waitFor();  
            System.out.println("ExitValue: " + exitVal);  
            fos.flush();  
            fos.close();  
        } catch (Throwable t) {  
            t.printStackTrace();  
        }  
    }  
}  

参考资料:
When Runtime.exec() won’t

转自:http://berdy.iteye.com/blog/810223

`Runtime.getRuntime().exec()`是Java中用于执行外部进程的方法。它返回一个Process对象,可以使用这个对象控制和监视进程的执行。 语法如下: ```java public Process exec(String command) throws IOException ``` 其中,command是要执行的命令。 例如,执行一个简单的命令: ```java Process process = Runtime.getRuntime().exec("ls"); ``` 这个代码将在当前目录下执行`ls`命令,并返回一个Process对象。 然而,这只是一个简单的例子。实际上,在执行外部进程时,还需要考虑很多其他方面,例如进程的输入、输出、错误处理等。 下面是一个更复杂的例子,演示如何使用`Runtime.getRuntime().exec()`执行一个Python脚本,并读取它的输出: ```java try { Process process = Runtime.getRuntime().exec("python myscript.py"); BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream())); String line; while ((line = reader.readLine()) != null) { System.out.println(line); } } catch (IOException e) { e.printStackTrace(); } ``` 在这个例子中,我们使用`BufferedReader`读取进程的输出,并将其打印到控制台上。如果进程执行时发生错误,我们可以使用`process.getErrorStream()`方法获取错误流,并对其进行处理。 需要注意的是,`Runtime.getRuntime().exec()`方法在执行外部进程时可能会阻塞当前线程,因此在处理输出时需要开启一个新的线程。此外,还需要注意处理输入、输出、错误流的顺序,以避免死锁等问题。 总之,`Runtime.getRuntime().exec()`是一个非常强大的方法,可以在Java中轻松地执行外部命令和脚本。但是,它需要仔细处理各种输入、输出、错误流,以确保进程能够正确执行。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值