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

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

1.自动执行命令的交互
   
public class Demo {

	public static void main(String[] args) {
		Process process=null;
		BufferedOutputStream out=null;
		BufferedInputStream in=null;
		try {
			process=Runtime.getRuntime().exec("sqlplus ethiopia1103/ethiopia1103@db90");
			out=new BufferedOutputStream(process.getOutputStream());
			in=new BufferedInputStream(process.getInputStream());
			out.write("exit".getBytes());
			out.write("\r\n".getBytes());
			out.flush();
			BufferedReader br=new BufferedReader(new InputStreamReader(in));
			String line=null;
			while((line=br.readLine())!=null){
				if(line.indexOf(">")!=-1) break;
				System.out.println(line);
			}
		}catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			try {
				if(null!=out){
					out.close();
					out=null;
				}
				if(null!=in){
					in.close();
					in=null;
				}
				int value=process.waitFor();
				if(null!=process)
					process.destroy();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

试试直接就退出了,证明传进去的exit命令起作用了。之所以用sqlplus做替代的命令测试,因为其和我要做的第三方接口命令类似。
然后依据这个简单的测试封装了一个命令交互的类。直接上代码:
public class DosCommandInteraction {

	private static Logger logger=Logger.getLogger(DosCommandInteraction.class);

	private static final String ENTER="\r\n";   //每次输入命令后回车,然后命令执行,出结果
	private static final String END="> ";   //遇到>时就退出,证明上一个命令已经执行完
	private static final String ERROR="ERROR";   //登录时报ERROR就证明已经登录失败

	private Process process=null;
	private BufferedOutputStream out=null;
	private BufferedInputStream in=null;

	/**
	 * 登录到该命令下,创建执行命令的环境进程
	 * @param command 登陆命令
	 */
	public boolean loggin(String command){
		boolean flag=true;
		try {
			process=Runtime.getRuntime().exec(command);
			out=new BufferedOutputStream(process.getOutputStream());
			in=new BufferedInputStream(process.getInputStream());
			String result=writeCommandResult(in);   //把登录时的信息取出来,判断是否登录成功!其实也为后面能正常输出命令结果做了清理
			String[] lines=result.split(ENTER);
			for(String line :lines){
				if(line.indexOf(ERROR)!=-1){
					flag=false;
					break;
				}
			}
		}catch (IOException e) {
			// TODO Auto-generated catch block
			logger.error(e);
			close();
		}
		if(!flag) close();
		return flag;
	}

	/**
	 * 将输入的命令转化为流执行命令得到执行的记录
	 * @param command
	 * @return
	 */
	public List<String> execCommand(String command){   
		logger.info("exec command : "+command);
		InputStream input = new ByteArrayInputStream((command+ENTER).getBytes());
		readerCommand(out,input);
		String result=writeCommandResult(in);
		logger.info(result);
		try {
			input.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return Arrays.asList(result.split(ENTER));
	}

	/**
	 * 将命令写入输出流
	 * @param outs	全局输出流
	 * @param ins	输入命令的流
	 */
	private void readerCommand(OutputStream outs,InputStream ins){
		int ch;
		try {
			while ((ch = ins.read()) != -1) {
				outs.write(ch);
				outs.flush();
			}
		} catch (IOException e) {
			close();
			logger.error("readerCommand",e);
		}
	}

	/**
	 * 读取命令返回的结果
	 * @param ins 全局的输入流
	 * @return 命令结果
	 */
	private String writeCommandResult(InputStream ins){
		int length = -1;
		byte[] buffer = new byte[10240];
		String readLine = null;
		StringBuilder readResult = new StringBuilder("");
		try {
	        while((length=ins.read(buffer))>0){
	        	readLine = new String(buffer, 0 , length,"gbk");
	        	readResult.append(readLine);
	        	if(readLine.indexOf(ERROR)!=-1) break;
	            if(readResult.toString().endsWith(END)||readResult.toString().endsWith(END.trim()))
	            	break;
	        }
		} catch (IOException e) {
			close();
			logger.error("writeCommandResult",e);
		}
		return readResult.toString();
	}

	/**
	 * 所有命令执行完成后推出命令,关闭进程
	 */
	public void quit(){
		execCommand("quit");
		close();
	}

	/**
	 * 关闭所有的流和进程
	 */
	private void close(){
		try {
			if(null!=out){
				out.close();
				out=null;
			}
			if(null!=in){
				in.close();
				in=null;
			}
			int value=process.waitFor();
			logger.info("process end state :" +value);
			if(null!=process)
				process.destroy();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			logger.error(e);
		} catch (InterruptedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

我在配置文件里设置如下:
<bean id="command" class="java.util.ArrayList">
		<constructor-arg>
			<list>
				<value>sqlplus orcl/orcl@db</value>
				<value>select 1 from dual;</value>
				<value>select 2 from dual;</value>
				<value>select 3 from dual;</value>
				<value>select 4 from dual;</value>
			</list>
		</constructor-arg>
	</bean>

测试方法:
public void handler(){
		List<String> commList=(List<String>) SpringUtil.getObject("command");
		logger.info("start.................");
		DosCommandInteraction dos=new DosCommandInteraction();
		if(!dos.loggin(commList.get(0))){
			logger.info("connection error!");
			return;
		}
		for(int i=1;i<commList.size();i++)
			dos.execCommand(commList.get(i));
		dos.quit();
		logger.info("end.................");
	}

测试结果如下:
main [2011-06-27 16:33:59] - start.................
main [2011-06-27 16:33:59] - exec command : select 1 from dual;
main [2011-06-27 16:33:59] - 
	 1
----------
	 1

SQL> 
main [2011-06-27 16:33:59] - exec command : select 2 from dual;
main [2011-06-27 16:33:59] - 
	 2
----------
	 2

SQL> 
main [2011-06-27 16:33:59] - exec command : select 3 from dual;
main [2011-06-27 16:33:59] - 
	 3
----------
	 3

SQL> 
main [2011-06-27 16:33:59] - exec command : select 4 from dual;
main [2011-06-27 16:33:59] - 
	 4
----------
	 4

SQL> 
main [2011-06-27 16:33:59] - exec command : quit
main [2011-06-27 16:33:59] - 从 Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning, OLAP and Data Mining options 断开

main [2011-06-27 16:33:59] - process end state :0
main [2011-06-27 16:33:59] - end.................


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


2.如果是想直接敲命令的互动,可是尝试如下:
public class TwoDemo {

	public static void main(String[] args) {
		Process process = null;
		BufferedOutputStream out = null;
		BufferedInputStream in = null;
		try {
			process = Runtime.getRuntime().exec(
					"sqlplus orcl/orcl@db");
			out = new BufferedOutputStream(process.getOutputStream());
			in = new BufferedInputStream(process.getInputStream());
			readWrite(in, out, System.in, System.out);  //该方法借用common-net的测试例子部分的一个工具类的方法
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				if (null != out) {
					out.close();
					out = null;
				}
				if (null != in) {
					in.close();
					in = null;
				}
				int value = process.waitFor();
				if (null != process)
					process.destroy();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	public static final void readWrite(final InputStream remoteInput,
			final OutputStream remoteOutput, final InputStream localInput,
			final OutputStream localOutput) {
		Thread reader, writer;

		reader = new Thread() {
			@Override
			public void run() {
				int ch;

				try {
					while (!interrupted() && (ch = localInput.read()) != -1) {
						remoteOutput.write(ch);
						remoteOutput.flush();
					}
				} catch (IOException e) {
					// e.printStackTrace();
				}
			}
		};

		writer = new Thread() {
			@Override
			public void run() {
				try {
					Util.copyStream(remoteInput, localOutput);
				} catch (IOException e) {
					e.printStackTrace();
					System.exit(1);
				}
			}
		};

		writer.setPriority(Thread.currentThread().getPriority() + 1);

		writer.start();
		reader.setDaemon(true);
		reader.start();

		try {
			writer.join();
			reader.interrupt();
		} catch (InterruptedException e) {
		}
	}
}

看看测试结果:
SQL*Plus: Release 10.2.0.1.0 - Production on 星期一 6月 27 16:45:58 2011

Copyright (c) 1982, 2005, Oracle.  All rights reserved.


连接到: 
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning, OLAP and Data Mining options

SQL> select * from dual;

DU
--
X

SQL> select 1 from dual;

	 1
----------
	 1

SQL> exit
从 Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning, OLAP and Data Mining options 断开

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


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



有什么问题希望指正。

猜你喜欢

转载自zzldn.iteye.com/blog/1106647