使用Process进行DOS命令交互的简单应用

继上一篇 Process应用之惑 后,继续在为此不断修改,后来因为需求变化,又开始了process的进步一发掘。 
    先交代下背景。第三方软件发布了命令接口,根据执行发布的命令,可以得到第三方硬件的信息。而我现在需要软件直接执行我排好序的命令,以便获取硬件信息,这时需要做个远程的命令登陆,然后远程执行命令,再退出。这其实就好比要模拟telnet、ftp等的客户端,当远程登陆后执行一个指令,然后返回一大堆执行结果,从而实现与客户端的命令交互。 
    起先,去下了个common-net.jar的源码看了看,发现telnet、ftp等通信协议都是有专门的消息通道,产生专门的端口来做通讯传输。而这个第三方的命令接口只是个在dos下能执行命令的客户端,再说,即使人家做成了类似telnet的通信协议,俺也不可能知道人家用的端口啥啥的。那现在唯一能做的是,模拟DOS窗口,进行命令的交互。 
    有了这个思路,就又想到Process可是能执行命令的,那这个是不是可以呢!Process是为了执行命令而产生的一个独立的执行线程。理论上如果只要这个线程不被销毁,那么会一直可以执行命令。可是Process三个流InputStream、OutputStream、ErrorStream在API上说的是提供输出信息的,未知获得了这输入、输出流是否能干出一番事业呢! 

1.自动执行命令的交互 
   
Java代码   收藏代码
  1. public class Demo {  
  2.   
  3.     public static void main(String[] args) {  
  4.         Process process=null;  
  5.         BufferedOutputStream out=null;  
  6.         BufferedInputStream in=null;  
  7.         try {  
  8.             process=Runtime.getRuntime().exec("sqlplus ethiopia1103/ethiopia1103@db90");  
  9.             out=new BufferedOutputStream(process.getOutputStream());  
  10.             in=new BufferedInputStream(process.getInputStream());  
  11.             out.write("exit".getBytes());  
  12.             out.write("\r\n".getBytes());  
  13.             out.flush();  
  14.             BufferedReader br=new BufferedReader(new InputStreamReader(in));  
  15.             String line=null;  
  16.             while((line=br.readLine())!=null){  
  17.                 if(line.indexOf(">")!=-1break;  
  18.                 System.out.println(line);  
  19.             }  
  20.         }catch (IOException e) {  
  21.             // TODO Auto-generated catch block  
  22.             e.printStackTrace();  
  23.         }finally{  
  24.             try {  
  25.                 if(null!=out){  
  26.                     out.close();  
  27.                     out=null;  
  28.                 }  
  29.                 if(null!=in){  
  30.                     in.close();  
  31.                     in=null;  
  32.                 }  
  33.                 int value=process.waitFor();  
  34.                 if(null!=process)  
  35.                     process.destroy();  
  36.             } catch (IOException e) {  
  37.                 // TODO Auto-generated catch block  
  38.                 e.printStackTrace();  
  39.             } catch (InterruptedException e) {  
  40.                 // TODO Auto-generated catch block  
  41.                 e.printStackTrace();  
  42.             }  
  43.         }  
  44.     }  
  45. }  

试试直接就退出了,证明传进去的exit命令起作用了。之所以用sqlplus做替代的命令测试,因为其和我要做的第三方接口命令类似。 
然后依据这个简单的测试封装了一个命令交互的类。直接上代码: 
Java代码   收藏代码
  1. public class DosCommandInteraction {  
  2.   
  3.     private static Logger logger=Logger.getLogger(DosCommandInteraction.class);  
  4.   
  5.     private static final String ENTER="\r\n";   //每次输入命令后回车,然后命令执行,出结果  
  6.     private static final String END="> ";   //遇到>时就退出,证明上一个命令已经执行完  
  7.     private static final String ERROR="ERROR";   //登录时报ERROR就证明已经登录失败  
  8.   
  9.     private Process process=null;  
  10.     private BufferedOutputStream out=null;  
  11.     private BufferedInputStream in=null;  
  12.   
  13.     /** 
  14.      * 登录到该命令下,创建执行命令的环境进程 
  15.      * @param command 登陆命令 
  16.      */  
  17.     public boolean loggin(String command){  
  18.         boolean flag=true;  
  19.         try {  
  20.             process=Runtime.getRuntime().exec(command);  
  21.             out=new BufferedOutputStream(process.getOutputStream());  
  22.             in=new BufferedInputStream(process.getInputStream());  
  23.             String result=writeCommandResult(in);   //把登录时的信息取出来,判断是否登录成功!其实也为后面能正常输出命令结果做了清理  
  24.             String[] lines=result.split(ENTER);  
  25.             for(String line :lines){  
  26.                 if(line.indexOf(ERROR)!=-1){  
  27.                     flag=false;  
  28.                     break;  
  29.                 }  
  30.             }  
  31.         }catch (IOException e) {  
  32.             // TODO Auto-generated catch block  
  33.             logger.error(e);  
  34.             close();  
  35.         }  
  36.         if(!flag) close();  
  37.         return flag;  
  38.     }  
  39.   
  40.     /** 
  41.      * 将输入的命令转化为流执行命令得到执行的记录 
  42.      * @param command 
  43.      * @return 
  44.      */  
  45.     public List<String> execCommand(String command){     
  46.         logger.info("exec command : "+command);  
  47.         InputStream input = new ByteArrayInputStream((command+ENTER).getBytes());  
  48.         readerCommand(out,input);  
  49.         String result=writeCommandResult(in);  
  50.         logger.info(result);  
  51.         try {  
  52.             input.close();  
  53.         } catch (IOException e) {  
  54.             // TODO Auto-generated catch block  
  55.             e.printStackTrace();  
  56.         }  
  57.         return Arrays.asList(result.split(ENTER));  
  58.     }  
  59.   
  60.     /** 
  61.      * 将命令写入输出流 
  62.      * @param outs  全局输出流 
  63.      * @param ins   输入命令的流 
  64.      */  
  65.     private void readerCommand(OutputStream outs,InputStream ins){  
  66.         int ch;  
  67.         try {  
  68.             while ((ch = ins.read()) != -1) {  
  69.                 outs.write(ch);  
  70.                 outs.flush();  
  71.             }  
  72.         } catch (IOException e) {  
  73.             close();  
  74.             logger.error("readerCommand",e);  
  75.         }  
  76.     }  
  77.   
  78.     /** 
  79.      * 读取命令返回的结果 
  80.      * @param ins 全局的输入流 
  81.      * @return 命令结果 
  82.      */  
  83.     private String writeCommandResult(InputStream ins){  
  84.         int length = -1;  
  85.         byte[] buffer = new byte[10240];  
  86.         String readLine = null;  
  87.         StringBuilder readResult = new StringBuilder("");  
  88.         try {  
  89.             while((length=ins.read(buffer))>0){  
  90.                 readLine = new String(buffer, 0 , length,"gbk");  
  91.                 readResult.append(readLine);  
  92.                 if(readLine.indexOf(ERROR)!=-1break;  
  93.                 if(readResult.toString().endsWith(END)||readResult.toString().endsWith(END.trim()))  
  94.                     break;  
  95.             }  
  96.         } catch (IOException e) {  
  97.             close();  
  98.             logger.error("writeCommandResult",e);  
  99.         }  
  100.         return readResult.toString();  
  101.     }  
  102.   
  103.     /** 
  104.      * 所有命令执行完成后推出命令,关闭进程 
  105.      */  
  106.     public void quit(){  
  107.         execCommand("quit");  
  108.         close();  
  109.     }  
  110.   
  111.     /** 
  112.      * 关闭所有的流和进程 
  113.      */  
  114.     private void close(){  
  115.         try {  
  116.             if(null!=out){  
  117.                 out.close();  
  118.                 out=null;  
  119.             }  
  120.             if(null!=in){  
  121.                 in.close();  
  122.                 in=null;  
  123.             }  
  124.             int value=process.waitFor();  
  125.             logger.info("process end state :" +value);  
  126.             if(null!=process)  
  127.                 process.destroy();  
  128.         } catch (IOException e) {  
  129.             // TODO Auto-generated catch block  
  130.             logger.error(e);  
  131.         } catch (InterruptedException e) {  
  132.             // TODO Auto-generated catch block  
  133.             e.printStackTrace();  
  134.         }  
  135.     }  
  136. }  

我在配置文件里设置如下: 
Java代码   收藏代码
  1. <bean id="command" class="java.util.ArrayList">  
  2.         <constructor-arg>  
  3.             <list>  
  4.                 <value>sqlplus orcl/orcl@db</value>  
  5.                 <value>select 1 from dual;</value>  
  6.                 <value>select 2 from dual;</value>  
  7.                 <value>select 3 from dual;</value>  
  8.                 <value>select 4 from dual;</value>  
  9.             </list>  
  10.         </constructor-arg>  
  11.     </bean>  

测试方法: 
Java代码   收藏代码
  1. public void handler(){  
  2.         List<String> commList=(List<String>) SpringUtil.getObject("command");  
  3.         logger.info("start.................");  
  4.         DosCommandInteraction dos=new DosCommandInteraction();  
  5.         if(!dos.loggin(commList.get(0))){  
  6.             logger.info("connection error!");  
  7.             return;  
  8.         }  
  9.         for(int i=1;i<commList.size();i++)  
  10.             dos.execCommand(commList.get(i));  
  11.         dos.quit();  
  12.         logger.info("end.................");  
  13.     }  

测试结果如下: 
Java代码   收藏代码
  1. main [2011-06-27 16:33:59] - start.................  
  2. main [2011-06-27 16:33:59] - exec command : select 1 from dual;  
  3. main [2011-06-27 16:33:59] -   
  4.      1  
  5. ----------  
  6.      1  
  7.   
  8. SQL>   
  9. main [2011-06-27 16:33:59] - exec command : select 2 from dual;  
  10. main [2011-06-27 16:33:59] -   
  11.      2  
  12. ----------  
  13.      2  
  14.   
  15. SQL>   
  16. main [2011-06-27 16:33:59] - exec command : select 3 from dual;  
  17. main [2011-06-27 16:33:59] -   
  18.      3  
  19. ----------  
  20.      3  
  21.   
  22. SQL>   
  23. main [2011-06-27 16:33:59] - exec command : select 4 from dual;  
  24. main [2011-06-27 16:33:59] -   
  25.      4  
  26. ----------  
  27.      4  
  28.   
  29. SQL>   
  30. main [2011-06-27 16:33:59] - exec command : quit  
  31. main [2011-06-27 16:33:59] - 从 Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production  
  32. With the Partitioning, OLAP and Data Mining options 断开  
  33.   
  34. main [2011-06-27 16:33:59] - process end state :0  
  35. main [2011-06-27 16:33:59] - end.................  


真正做到了自动执行命令,并且获取到该命令的结果。 


2.如果是想直接敲命令的互动,可是尝试如下: 
Java代码   收藏代码
  1. public class TwoDemo {  
  2.   
  3.     public static void main(String[] args) {  
  4.         Process process = null;  
  5.         BufferedOutputStream out = null;  
  6.         BufferedInputStream in = null;  
  7.         try {  
  8.             process = Runtime.getRuntime().exec(  
  9.                     "sqlplus orcl/orcl@db");  
  10.             out = new BufferedOutputStream(process.getOutputStream());  
  11.             in = new BufferedInputStream(process.getInputStream());  
  12.             readWrite(in, out, System.in, System.out);  //该方法借用common-net的测试例子部分的一个工具类的方法  
  13.         } catch (IOException e) {  
  14.             // TODO Auto-generated catch block  
  15.             e.printStackTrace();  
  16.         } finally {  
  17.             try {  
  18.                 if (null != out) {  
  19.                     out.close();  
  20.                     out = null;  
  21.                 }  
  22.                 if (null != in) {  
  23.                     in.close();  
  24.                     in = null;  
  25.                 }  
  26.                 int value = process.waitFor();  
  27.                 if (null != process)  
  28.                     process.destroy();  
  29.             } catch (IOException e) {  
  30.                 // TODO Auto-generated catch block  
  31.                 e.printStackTrace();  
  32.             } catch (InterruptedException e) {  
  33.                 // TODO Auto-generated catch block  
  34.                 e.printStackTrace();  
  35.             }  
  36.         }  
  37.     }  
  38.   
  39.     public static final void readWrite(final InputStream remoteInput,  
  40.             final OutputStream remoteOutput, final InputStream localInput,  
  41.             final OutputStream localOutput) {  
  42.         Thread reader, writer;  
  43.   
  44.         reader = new Thread() {  
  45.             @Override  
  46.             public void run() {  
  47.                 int ch;  
  48.   
  49.                 try {  
  50.                     while (!interrupted() && (ch = localInput.read()) != -1) {  
  51.                         remoteOutput.write(ch);  
  52.                         remoteOutput.flush();  
  53.                     }  
  54.                 } catch (IOException e) {  
  55.                     // e.printStackTrace();  
  56.                 }  
  57.             }  
  58.         };  
  59.   
  60.         writer = new Thread() {  
  61.             @Override  
  62.             public void run() {  
  63.                 try {  
  64.                     Util.copyStream(remoteInput, localOutput);  
  65.                 } catch (IOException e) {  
  66.                     e.printStackTrace();  
  67.                     System.exit(1);  
  68.                 }  
  69.             }  
  70.         };  
  71.   
  72.         writer.setPriority(Thread.currentThread().getPriority() + 1);  
  73.   
  74.         writer.start();  
  75.         reader.setDaemon(true);  
  76.         reader.start();  
  77.   
  78.         try {  
  79.             writer.join();  
  80.             reader.interrupt();  
  81.         } catch (InterruptedException e) {  
  82.         }  
  83.     }  
  84. }  

看看测试结果: 
Java代码   收藏代码
  1. SQL*Plus: Release 10.2.0.1.0 - Production on 星期一 6月 27 16:45:58 2011  
  2.   
  3. Copyright (c) 19822005, Oracle.  All rights reserved.  
  4.   
  5.   
  6. 连接到:   
  7. Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production  
  8. With the Partitioning, OLAP and Data Mining options  
  9.   
  10. SQL> select * from dual;  
  11.   
  12. DU  
  13. --  
  14. X  
  15.   
  16. SQL> select 1 from dual;  
  17.   
  18.      1  
  19. ----------  
  20.      1  
  21.   
  22. SQL> exit  
  23. 从 Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production  
  24. With the Partitioning, OLAP and Data Mining options 断开  

和在dos下执行是完全一样的 


产生process = Runtime.getRuntime().exec()时为什么要选择这种方式呢?为什么不是ProcessBuilder了呢? 
我们现在不论使用哪种方式产生的命令执行进程都会读取进程的流,所以就不会再有流堵塞而导致无法执行下去的问题了。 
ProcessBuilder在加入命令时是以数组的形式,如果是sqlplus orcl/orcl@db就需要分为两个参数加入,而现在我们更希望是一个命令是一个字符串。 



有什么问题希望指正。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值