java Runtime.exec方法详解!

在做一个项目时用到了Runtime.exec(String command),但是网上的很多博客讲解的都很不详细明白,也不全,干脆我自己来做一个讲解。

我们先来看看api的解释:

从字面上理解的意思就是在单独打开一条线程执行指定的命令。当然,这个方法有多个重载,如下:

下面会讲到其它的重载方法,不急。

想到api的解释我第一时间想到的就是cmd命令行!因为确实有些相似,但是转念一想又不对,“在单独的进程中执行指定的字符串命令”不就是这玩意儿吗!


然后我分别测试了这几个命令“explorer”,“calc”,“notepad”。都能正确的打开资源管理器、计算器和记事本程序。

但是cmd命令就不一样了,cmd命令不会弹出窗口,可能是以上几个命令是调用的外部的exe文件,而cmd不是吧,具体机制我不清楚,调用cmd命令呢在网上我看到了如下几种方法:


cmd /c dir 是执行完dir命令后关闭命令窗口。

cmd /k dir 是执行完dir命令后不关闭命令窗口。

cmd /c start dir 会打开一个新窗口后执行dir指令,原窗口会关闭。

cmd /k start dir 会打开一个新窗口后执行dir指令,原窗口不会关闭。


/c start 和 /k start 我没测试过,主要使用的是/c,/k我测试了很多次,使用/k的话它怎么都获取不到输出的信息,无论是输入流还是错误输入流都不行!但是用/c就能正常获取,所以我一直使用的是/c。


然后,重点来了!划重点了!

谁说的调用一次exec方法不能执行多条cmd命令!

rt.exec("cmd /c set CLASSPATH=D:\\ && javac D:\\a.java && java a");


一个“&&”完美搞定~~

实测连接三条命令没问题,上限没测试过,各位看官无聊了可以去试试~~


但是,该方法有局限性,它只能在cmd里面使用,也就是前面必须有cmd三个字,你可以自己试试win+r调用cmd然后用&&连接多条命令执行,经过我的实测,是没有问题的,也就是说exec方法本质上还是只能调用一条命令的,我们能一次执行多条命令实际上是利用了cmd的语法的好处。


在上述代码中我先是设置了类路径为D盘根目录,完了编译a.java文件,紧接着运行a.class,在这里如果不设置classpath属性的话会导致运行a.class时报找不到类文件的错误。


可能大家已经觉得了,在上面的代码中我要设置classpath的属性又要javac一个完整的绝对路径,有没有什么方法能够一劳永逸呢?别说,还真有,这里就会用到其它几个重载的exec方法了。在这里我们只讲一个方法,因为其它的方法包括以上的那个方法最终都是调用它。


我们来一个参数一个参数的详解,第一个参数cmdarray——包含所调用命令极其参数的数组。


例如:shutdown -s -t 3600——在一小时后自动关机命令


我们可以构建这样的一个数组:String arr[] = {"shutdown","-s","-t","3600"};

数组第一个元素“shutdown”是命令,其后的三个-s、-t、3600都是参数,然后直接将这个数组注入exec方法即可。


需要注意的是,在调用这个方法时我们不能将命令和参数放在一起——String arr[] = {"shutdown -s -t 3600"};


这样去调用的话程序会把“shutdown -s -t 3600”当成是一条命令的名称,它会去查找“shutdown -s -t 3600”这条命令,它当然会找不到,所以就会报错,想要偷懒的话我们可以调用这个方法,我自己平时也是调用的这个方法,比较方便:

这个重载的方法会自动将一条字符串分解成命令和参数,最后再去调用我们现在讲解的这个方法。


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


这个参数说白了就是去设置当前进程的环境变量,在上面的连接多条命令的例子中我们设置了classpath的路径,classpath其实就是一个环境变量,所以我们在这里就可以构建一个这样的数组:

String envp[] = {"CLASSPATH=D://"}


然后注入进exec方法即可,这样的话就可以直接java a去运行a.class了,程序会到D盘的根目录下去寻找a.class文件。


但是在我的测试中发现了一个很怪异的现象就是如果你在调用该方法的时候提供了这个参数,并且命令是cmd开头也就是在cmd中执行命令的时候,系统的环境变量就好像不存在了一样!你的环境变量就只剩下你数组提供的了。但是如果你不在cmd中执行命令,而是直接执行命令的话系统的环境变量又能发挥工作。


最后是第三个参数:dir——子进程的工作目录;如果子进程应该继承当前进程的工作目录,则该参数为 null。

其实就是设置当前的目录,一般要和cmd配合才能起作用,因为如果不在cmd里面执行命令的话,设置了目录也无济于事,程序依旧只会去环境变量设置的目录中寻找程序。


接下来我们就把上面的加粗的例子用这个方法调用:

String arr[] = {"CLASSPATH=D://","Path=C:\\Program Files\\Java\\jdk1.8.0_131\\bin"};  
  
rt.exec("cmd /c javac a.java && java a", arr, new File("D://"));


在上述代码中我设置的环境变量数组使得命令能找到a.class和java这两个文件,因为第二个参数的怪异性,所以我这里还要设置java文件路径才能正常工作,最后设置的目录使得命令能找到a.java文件。


最后,大家应该注意到了,exec方法还有一个返回的对象Process,这个对象是给我们用来控制exec方法创建的进程的,该对象可用来控制进程并获得相关信息。Process 类提供了执行从进程输入、执行输出到进程、等待进程完成、检查进程的退出状态以及销毁(杀掉)进程的方法。 我将在下篇文章详细讲解Process类,我们下次再见~~拜拜~~

发布于 2018-02-03 00:09