Java Runtime 执行系统命令行程序

4 篇文章 0 订阅

以前写过一篇文章,介绍通过 Java 的 Runtime 类执行操作系统命令行程序:Java调用linux系统shell执行命令。最近项目中又有需要用这个方法,在使用过程中遇到了一些新的问题,感觉以前没有弄清楚,故在此做补充学习记录。

先说明一下这次的需求,在 Java 程序中控制 Hadoop 命令执行 MapReduce 作业,并获取其输出内容。本来没有什么特殊,但由于 MR 执行的是 Kmeans 算法,会产递归产生多个 MR 程序,在捕获输出的时候就只有简单的几句提示,没有 MR 作业的详细信息。
经过查询后发现,执行过程中有一部分信息是作为普通信息输出的,另一部分则是作为 debug 信息输出的。如下图,用红线圈出来一行为普通输出(开始捕获到的只有这一行),其它均为 debug 信息。

为了区别这两种信息,这里就要说一下 Runtime 这个东西了。 Java API 中对该类的解释是 public class java.lang.Runtime extends java.lang.Object。
        每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。可以通过 getRuntime 方法获取当前运行时。应用程序不能创建自己的 Runtime 类实例。在API 文档 可以看到,执行过一个命令后,系统会返回一个新的 Process 对象,用于管理子进程。那么,这个新的 Process 对象又是何许东东,继续来看 API ,public abstract class java.lang.Process extends java.lang.Object。该实例可用来控制进程并获得相关信息。
        并且,Process 类提供了执行从进程输入、执行输出到进程、等待进程完成、检查进程的退出状态以及销毁(杀掉)进程的方法。
        它的所有标准 io(即 stdin、stdout 和 stderr)操作都将通过三个流 (getOutputStream()、getInputStream() 和 getErrorStream()) 重定向到父进程。
        父进程使用这些流来提供到子进程的输入和获得从子进程的输出。因为有些本机平台仅针对标准输入和输出流提供有限的缓冲区大小,如果读写子进程的输出流或输入流迅速出现失败,则可能导致子进程阻塞,甚至产生死锁。

现在问题就明显了。以前只获取了 Process.getInputStream() 的 InputStream 对象,没有用 process.getErrorStream() 获取进程的错误输出流传送的数据。
        本来是想通过 while 循环来输出两个流的数据,但是发现在利用 BufferedReader 读取 Process stream 流的时候,当使 stream 为空(还没产生,并不是 null ),那么 BufferedReader 则会被阻塞。
        所以想了一下还是得用多线程的方式同时输出两个流的内容。
        那么,代码更新一下,变成如下的方式。

package com.cz.shell;

import java.io.BufferedReader;
import java.io.InputStreamReader;


class CzStreamOutput extends Thread {
	public BufferedReader br;


	public CzStreamOutput() {
	}


	public CzStreamOutput(BufferedReader br) {
		this.br = br;
	}


	public void run() {
		String line;
		try {
			while ((line = br.readLine()) != null) {
				System.out.println(line);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}


public class CzShell {
	public static void runShell(String cmd) throws Exception {
		Process process = null;
		try {
			process = Runtime.getRuntime().exec(cmd);
			BufferedReader bri = new BufferedReader(new InputStreamReader(
					process.getInputStream()));
			BufferedReader bre = new BufferedReader(new InputStreamReader(
					process.getErrorStream()));
			new CzStreamOutput(bri).start();
			new CzStreamOutput(bre).start();
			process.waitFor();
			bri.close();
			bre.close();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			process.destroy();
		}
	}
}

 

在 Linux (Centos 6.2)上运行测试,一切顺利。不过在此说明一下,由于是多线程,理论上会出现一些错误流和输出流顺序颠倒的情况,但是对于多数命令来说,系统的时钟频率要比命令执行时两个流的间隔高很多,出现混乱的可能性极小。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
java概述 计算机能识别的语言: 1.机器语言:由0和1组成可以被电脑理解。 2.汇编语言:由汇编软件把汇编语言翻译成机器语言 3.高级语言:由高级语言的编译器来把高级语言翻译成机器语言,然后计算机来运行。如:java,c,c++。 java语言的特点: 1.简介有效:面向对象。 2.java跨平台:一次编译处处运行。java为每一个平台提供jvm(java virtual machine) 3.适合分布式项目:java语言具有强大的,易于使用的互联网能力,非常适合大型的互联网分布式项目。   java分支: 1.javase(标准版) 2.javame(微型版),给移动设备做支持,基本淘汰了 3.javaee(企业版) JRE和JDK概述和软件安装 JRE(java runtime environment) java运行环境。 JVM(java virtual machine     java虚拟机):java程序会载入JVM,然后运行。 java核心类库:java程序在运行的过程中需要依赖一些库文件。 JDK(java development kit)  java开发工具。 JDK包含JRE,我们直接安装JDK就可以了。   安装JDK 从oracle官网下载对应的JDK安装包(不要安装到中文目录,最好安装到c盘)。     JDK是通过命令行来使用的。   JDK目录 bin目录(binary  二元的)二进制文件或可执行文件: java.exe:运行 javac.exe:编译   db目录(关于数据的存储)   include目录(本地接口编程): 包含C语言给java语言提供的一些接口支持   jre目录:java运行环境   lib目录:java开发工具包   zip文件是jdk里面的源码 jdk环境变量配置 环境变量 电脑右键属性——高级系统设置——高级——环境变量             path项:dos窗口下输入的命令会先当前目录下查找命令。若没有,会在path项里的目录下查找命令是否存在。 当需要运行某些程序命令时会先在当前文件夹中寻找,如果当前目录下找不到,就会到设置的默认路径中去查找,如果发现在默认路径中找到程序的位置,就直接运行,这样就实现程序命令随叫随到。这个“默认路径”就是环境变量。 内部命令指的是当前目录下存在的命令。 外部命令指的是path目录下存在的命令。 jdk的环境变量配置: 1.配置JAVA_HOME:在环境变量中系统变量里新建一项: 2.在Path中添加如下目录 +     classpath的配置 classpath是我们类存放的根路径,我们可以设置classpath来在任意路径来执行java的class类 .;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar;(.   代表当前路径) java运行机制、java标识符 java标识符 1.在java语言中,对各种变量,方法和类等要素命名时所使用的字符序列就是标识符。 有些标识符是jdk定义好的(关键字。 如:public) 2.java中标识符由26个英文字符大小写,数字0-9,符号_$。注意:数字不能开头。 3.java中是严格区分大小写。 4.在实际写程序的过程中定义标识符遵守“见名之意”。   类名和接口的规范: 每个单词的首字母大写。(驼峰模式)XxxYyy   如:PersonService   变量名和方法名: 第一个首字母小写,其余的首字母大写。xxxYyyZzz。如:personDao   包名: 全小写。xxx.yyy.zzz   常量: 所有字符都大写,多个单词之间使用下划线分割 关键字 jdk定义的标识符就是关键字。 java的注释 多行注释:    /*。。。。*/ 单行注释:   // 文档注释:   /** ….*/

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值