使用JAVA调用操作系统命令,阻塞的原因

来源:互联网 发布:优化调整方案 编辑:程序博客网 时间:2024/05/29 21:18




Java代码  收藏代码
  1. import java.io.InputStream;  
  2. import java.util.ArrayList;  
  3.   
  4. public class JavaExcCommand {  
  5.     private static String INPUT_STREAM = "INPUTSTREAM";  
  6.     private static String ERROR_STREAM = "ERRORSTREAM";  
  7.   
  8.     /** 
  9.      * 返回命令执行结果信息串 
  10.      * @param command 要执行的命令 
  11.      * @return 第一个为标准信息,第二个为错误信息,如果不存在则相应为空 
  12.      * @throws Throwable String[] 
  13.      */  
  14.     public static String[] exec(String command) throws Throwable {  
  15.   
  16.         Process process = null;  
  17.         Runtime runtime = Runtime.getRuntime();  
  18.   
  19.         String osName = System.getProperty("os.name").toLowerCase();  
  20.         if (osName.indexOf("windows 9") > -1) {  
  21.             process = runtime.exec("command.com /c " + command);  
  22.         } else if ((osName.indexOf("nt") > -1)  
  23.                 || (osName.indexOf("windows 20") > -1)  
  24.                 || (osName.indexOf("windows xp") > -1 || (osName.indexOf("windows vista") > -1))) {  
  25.   
  26.             /* 
  27.              * 开关/C指明后面跟随的字符串是命令,并在执行命令后关闭DOS窗口,使用cmd /?查看帮助 
  28.              */  
  29.             process = runtime.exec("cmd.exe /c " + command);  
  30.         } else {  
  31.             // Linux,Unix  
  32.             process = runtime.exec(command);  
  33.         }  
  34.   
  35.         //存储返回结果,第一个为标准信息,第二个为错误信息  
  36.         String result[] = new String[2];  
  37.   
  38.         Object mutexInstream = new Object();  
  39.         Object mutexErrorstream = new Object();  
  40.         new ReadThread(process.getInputStream(), INPUT_STREAM, result, mutexInstream)  
  41.                 .start();  
  42.         new ReadThread(process.getErrorStream(), ERROR_STREAM, result, mutexErrorstream)  
  43.                 .start();  
  44.         //确保子线程已启动  
  45.         Thread.sleep(20);  
  46.   
  47.         /* 
  48.          * 这里一定要等标准流与错误都读完了后才能继续执行后面的代码,否则外面引用返回的结果可能 
  49.          * 为null或空串,所以要等两个线程执行完,这里确保读取的结果已返回。在读取时使用了两个线 
  50.          * 程,因为发现在一个线程里读这种流时,有时会阻塞,比如代码实现时先读取标准流,而运行时 
  51.          * 命令却执行失败,这时读标准流的动作会阻塞,导致程序最终挂起,先读错误流时如果执行时成 
  52.          * 功,这时又可能挂起。还有一个问题就是即使使用两个线程分别读取流,如果不使用同步锁时,也 
  53.          * 会有问题:主线程读不到子线程返回的数据,这主要是由于主线读取时子线还没未返回读取到的信 
  54.          * 息,又因为两个读线程不能互斥,但又要与主线程同步,所以使用了两个同步锁,这样两个线程谁 
  55.          * 先执行互不影响,而且主线程阻塞直到标准信息与错误信息都返回为止 
  56.          */  
  57.         synchronized (mutexInstream) {  
  58.             synchronized (mutexErrorstream) {  
  59.                 /* 
  60.                  * 导致当前线程等待,如果必要,一直要等到由该 Process 对象表示的进程已经终止 
  61.                  * 。如果已终止该子进程,此方法立即返回。如果没有终止该子进程,调用的线程将被 
  62.                  * 阻塞,直到退出子进程。   
  63.                  * process.waitFor()目的是等待子进程完成后再往下执行,不过这里好像没有什么 
  64.                  * 太大的作用除了用来判断返回的状态码外,因为如果程序进到这里表示子线程已执行完 
  65.                  * 毕,process子进程理所当然的也已执行完毕,如果子进程process未执行完,我想 
  66.                  * 读流的操作肯定会阻塞的。 
  67.                  *  
  68.                  * 另外,使用process.waitFor()要注的是一定不要在数据流读取前使用,否则线程 
  69.                  * 也会挂起,导致该现象的原因可能是该命令的输内容出比较多,而运行窗口的输出缓冲 
  70.                  * 区不够大,最后没不能写缓冲引起,所以这里先使用了两个单独的线程去读,这样不管 
  71.                  * 数据量有多大,都不会阻塞了。 
  72.                  */  
  73.                 if (process.waitFor() != 0) {  
  74.                     result[0] = null;  
  75.                 } else {  
  76.                     result[1] = null;  
  77.                 }  
  78.             }  
  79.         }  
  80.         return result;  
  81.     }  
  82.   
  83.     public static void main(String args[]) throws Throwable {  
  84.         if (args.length == 0) {  
  85.             System.out.println("Useage: \r\n    java JavaExcCommand <command>");  
  86.             return;  
  87.         }  
  88.         String[] result = JavaExcCommand.exec(args[0]);  
  89.         System.out.println("error info:---------------\r\n" + result[1]);  
  90.         System.out.println("std info:-----------------\r\n" + result[0]);  
  91.     }  
  92.   
  93.     /* 
  94.      * 标准流与错误流读取线程 
  95.      */  
  96.     private static class ReadThread extends Thread {  
  97.         private InputStream is;  
  98.         private String[] resultArr;  
  99.         private String type;  
  100.         private Object mutex;  
  101.   
  102.         public ReadThread(InputStream is, String type, String[] resultArr, Object mutex) {  
  103.             this.is = is;  
  104.             this.type = type;  
  105.             this.resultArr = resultArr;  
  106.             this.mutex = mutex;  
  107.         }  
  108.   
  109.         public void run() {  
  110.             synchronized (mutex) {  
  111.                 try {  
  112.                     int readInt = is.read();  
  113.                     ArrayList result = new ArrayList();  
  114.   
  115.                     /* 
  116.                      * 这里读取时我们不要使用字符流与缓冲流,发现执行某些命令时会阻塞,不 
  117.                      * 知道是什么原因。所有这里使用了最原始的流来操作,就不会出现问题。 
  118.                      */  
  119.                     while (readInt != -1) {  
  120.                         result.add(Byte.valueOf(String.valueOf((byte) readInt)));  
  121.                         readInt = is.read();  
  122.                     }  
  123.   
  124.                     byte[] byteArr = new byte[result.size()];  
  125.                     for (int i = 0; i < result.size(); i++) {  
  126.                         byteArr[i] = ((Byte) result.get(i)).byteValue();  
  127.                     }  
  128.                     if (ERROR_STREAM.equals(this.type)) {  
  129.                         resultArr[1] = new String(byteArr);  
  130.                     } else {  
  131.                         resultArr[0] = new String(byteArr);  
  132.                     }  
  133.   
  134.                 } catch (Exception e) {  
  135.                     e.printStackTrace();  
  136.                 }  
  137.             }  
  138.         }  
  139.     }  
  140. }  

 

比如传递一个参数为 dir/w ,运行结果如下:

 

error info:---------------
null
std info:-----------------
 驱动器 E 中的卷是 Work
 卷的序列号是 9876-AE7E

 E:\_\Test 的目录

[.]          [..]         .classpath   .project     [bin]        [lib]
[src]        
               2 个文件            904 字节
               5 个目录  5,636,612,096 可用字节

 

如果传一个不存在的命令 aaa ,结果如下:

 

error info:---------------
'aaa' 不是内部或外部命令,也不是可运行的程序
或批处理文件。

std info:-----------------
null

 

最后来一个查看操作系统的环境变量,注,这里不是使用的System.getXX来获取的,这是Java虚拟机设置的,我们这里是操作系统所设环境变量,如在window上输出 set ,或在 linux 上输入 env ,window上运行的结果如下:

 

error info:---------------
null
std info:-----------------
ALLUSERSPROFILE=C:\ProgramData
APPDATA=C:\Users\jiangzhengjun\AppData\Roaming
classpath=D:\java\jdk1.5.0_17/lib;.
CommonProgramFiles=C:\Program Files\Common Files
COMPUTERNAME=JZJ-PC
ComSpec=C:\Windows\system32\cmd.exe
FP_NO_HOST_CHECK=NO
HOMEDRIVE=C:
HOMEPATH=\Users\jiangzhengjun
java_home=D:\java\jdk1.5.0_17
LOCALAPPDATA=C:\Users\jiangzhengjun\AppData\Local
LOGONSERVER=\\JZJ-PC
NUMBER_OF_PROCESSORS=2
OS=Windows_NT
Path=D:\java\jdk1.5.0_17/bin\..\jre\bin\client;D:\java\jdk1.5.0_17/bin\..\jre\bin;D:\java\jdk1.5.0_17/bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files\Common Files\Thunder Network\KanKan\Codecs;C:\Program Files\Broadcom\Broadcom 802.11\Driver
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
PROCESSOR_ARCHITECTURE=x86
PROCESSOR_IDENTIFIER=x86 Family 6 Model 23 Stepping 6, GenuineIntel
PROCESSOR_LEVEL=6
PROCESSOR_REVISION=1706
ProgramData=C:\ProgramData
ProgramFiles=C:\Program Files
PROMPT=$P$G
PSModulePath=C:\Windows\system32\WindowsPowerShell\v1.0\Modules\
PUBLIC=C:\Users\Public
SESSIONNAME=Console
SystemDrive=C:
SystemRoot=C:\Windows
TEMP=C:\Users\JIANGZ~1\AppData\Local\Temp
TMP=C:\Users\JIANGZ~1\AppData\Local\Temp
USERDOMAIN=jzj-pc
USERNAME=jiangzhengjun
USERPROFILE=C:\Users\jiangzhengjun
windir=C:\Windows

今天作项目时发现了一个简洁一点的办法:

Java代码  收藏代码
  1. import java.io.InputStream;  
  2. import java.util.ArrayList;  
  3.   
  4. public class JavaExcCommand {  
  5.   
  6.     /** 
  7.      * 返回命令执行结果信息串 
  8.      *  
  9.      * @param command 
  10.      *            要执行的命令 
  11.      * @return 第一个为标准信息,第二个为错误信息 
  12.      * @throws Throwable 
  13.      *             String[] 
  14.      */  
  15.     public static String[] exec(String command) throws Throwable {  
  16.         Process process = null;  
  17.         Runtime runtime = Runtime.getRuntime();  
  18.   
  19.         // Linux,Unix  
  20.         process = runtime.exec(command);  
  21.   
  22.         // 存储返回结果,第一个为标准信息,第二个为错误信息  
  23.         String result[] = new String[2];  
  24.         ReadThread inputReadThread = new ReadThread(process.getInputStream());  
  25.         ReadThread errReadThread = new ReadThread(process.getErrorStream());  
  26.         inputReadThread.start();  
  27.         errReadThread.start();  
  28.   
  29.         //确保标准与错误流都读完时才向外界返回执行结果  
  30.         while (true) {  
  31.             if (inputReadThread.flag && errReadThread.flag) {  
  32.                 break;  
  33.             } else {  
  34.                 Thread.sleep(1000);  
  35.             }  
  36.         }  
  37.         result[0] = inputReadThread.getResult();  
  38.         result[1] = errReadThread.getResult();  
  39.         return result;  
  40.     }  
  41.   
  42.     public static void main(String args[]) throws Throwable {  
  43.         if (args.length == 0) {  
  44.             System.out.println("Useage: \r\n    java JavaExcCommand <command>");  
  45.             return;  
  46.         }  
  47.         String[] result = JavaExcCommand.exec(args[0]);  
  48.         System.out.println("error info:---------------\r\n" + result[1]);  
  49.         System.out.println("std info:-----------------\r\n" + result[0]);  
  50.     }  
  51.   
  52.     /* 
  53.      * 标准流与错误流读取线程 
  54.      */  
  55.     private static class ReadThread extends Thread {  
  56.         private InputStream is;  
  57.   
  58.         private ArrayList result = new ArrayList();  
  59.   
  60.         public boolean flag;// 流是否读取完毕  
  61.   
  62.         public ReadThread(InputStream is) {  
  63.             this.is = is;  
  64.         }  
  65.   
  66.         // 获取命令执行后输出信息,如果没有则返回空""字符串  
  67.         protected String getResult() {  
  68.             byte[] byteArr = new byte[result.size()];  
  69.             for (int i = 0; i < result.size(); i++) {  
  70.                 byteArr[i] = ((Byte) result.get(i)).byteValue();  
  71.             }  
  72.             return new String(byteArr);  
  73.         }  
  74.   
  75.         public void run() {  
  76.             try {  
  77.                 int readInt = is.read();  
  78.                 while (readInt != -1) {  
  79.                     result.add(Byte.valueOf(String.valueOf((byte) readInt)));  
  80.                     readInt = is.read();  
  81.                 }  
  82.   
  83.                 flag = true;// 流已读完  
  84.             } catch (Exception e) {  
  85.                 e.printStackTrace();  
  86.             }  
  87.         }  
  88.     }  
  89.   
  90. }  
0 0
原创粉丝点击