`

Java调用shell参考文章

 
阅读更多

重要参考文章(强力推荐,使用Process类调用外部程序必看的文章):
http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html?page=4
这篇文章讲的大概是Runtime.getRuntime()调用外部程序可能潜在的问题并给出如何解决的方法,逐步推进,是一篇不错的文章。
我的使用场景是在java中调用/bin/sh执行一些命令,并获取命令的执行结果。
Process p=Runtime.getRuntime().exec(String[] cmdArr);
因为我想要执行一些文本处理,涉及到多个程序,不可避免的需要管道操作,所以选择了上面的形式让/bin/sh代理执行管道功能。
不妨把p看成一个Java创建的与执行命令的外部进程(由/bin/sh创建)进行交互的进程:

                                            |----------------|                                        |-------------
p.getInputStream()-------|   Java创建的           |<-----Normal Output---|  /bin/sh创建的 |
                                            |    交互进程P            |                                       |   外部进程         |
p.getErrorStream()------ |                                |<------Error Output---|                          |
                                           |----------------|                                        |-------------|

从上图可以看到p的inputStream其实就是外部进程的标准输出,p的errorStream对应外部进程的标准错误。
引文建议使用两个同时处理进程P的inputStream和errorStream,这是必要的。
我第一次使用时,并没有同时处理,而是先处理错误输出,如果错误输出不为空,就直接结束。
否则再处理标准输出。
------这种做法会出现阻塞的现象---当进程P的输出很大时---不知道是怎么回事,据引文的说法,似乎是
不采用同时读时,你无法判断哪个流的数据最先开始,所以可能出现无法读取,一直阻塞的现象。
然而,当你采用同时读取的方法时,也有一些要注意的,而这在引文中并没有提到:
以下是引文给出的做法:

Listing 4.7 GoodWinRedirect.java

import java.util.*;
import java.io.*;
class StreamGobbler extends Thread
{
    InputStream is;
    String type;
    OutputStream os;
   
    StreamGobbler(InputStream is, String type)
    {
        this(is, type, null);
    }
    StreamGobbler(InputStream is, String type, OutputStream redirect)
    {
        this.is = is;
        this.type = type;
        this.os = redirect;
    }
   
    public void run()
    {
        try
        {
            PrintWriter pw = null;
            if (os != null)
                pw = new PrintWriter(os);
               
            InputStreamReader isr = new InputStreamReader(is);
            BufferedReader br = new BufferedReader(isr);
            String line=null;
            while ( (line = br.readLine()) != null)
            {
                if (pw != null)
                    pw.println(line);
                System.out.println(type + ">" + line);   
            }
            if (pw != null)
                pw.flush();
        } catch (IOException ioe)
            {
            ioe.printStackTrace(); 
            }
    }
}


Listing 4.8 TestExec.java

import java.util.*;
import java.io.*;
// class StreamGobbler omitted for brevity
public class TestExec
{
    public static void main(String args[])
    {
        if (args.length < 1)
        {
            System.out.println("USAGE: java TestExec \"cmd\"");
            System.exit(1);
        }
       
        try
        {
            String cmd = args[0];
            Runtime rt = Runtime.getRuntime();
            Process proc = rt.exec(cmd);
           
            // any error message?
            StreamGobbler errorGobbler = new
                StreamGobbler(proc.getErrorStream(), "ERR");           
           
            // any output?
            StreamGobbler outputGobbler = new
                StreamGobbler(proc.getInputStream(), "OUT");
               
            // kick them off
            errorGobbler.start();
            outputGobbler.start();
                                   
            // any error???
            int exitVal = proc.waitFor();
            System.out.println("ExitValue: " + exitVal);

           //这里有些问题
        } catch (Throwable t)
          {
            t.printStackTrace();
          }
    }
}
使用这种方法,有时候会出现异常----提示流已经被关闭了,
   BufferedReader br = new BufferedReader(isr);
            String line=null;
            //流被关闭,无法读取,这里会抛出异常
            while ( (line = br.readLine()) != null)
            {
                if (pw != null)
                    pw.println(line);
                System.out.println(type + ">" + line);   
            }
为什么呢?
我们可以想到,因为进程P相当于给我们创建了两个管道,但是对于管道的读取,作读取的线程是不知道流什么时候关闭的,当进程P关闭管道后,线程仍然在读取,所以会抛出异常。
合理的方法是主线程等待两个读取线程的结束。
即是在
// kick them off
            errorGobbler.start();
            outputGobbler.start();
                                   
            // any error???
            int exitVal = proc.waitFor();
            System.out.println("ExitValue: " + exitVal);
           //加入
           errorGobbler.join();
          outputGobbler.join();

int exitVal = proc.waitFor();//这句只是等待进程P的结束,而此时两个线程仍然在读取的,如果就此跳过,可能会抛出异常

分享到:
评论

相关推荐

    JAVA如何调用Shell脚本

    本篇文章主要介绍了JAVA如何调用Shell脚本,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

    AdbShellCommand:在Android项目中执行adb Shell命令,通过java代码调用执行,采用MVP架构演示样例

    ###Java中执行adb shell命令本项目中,adb shell命令执行的实现方法主要参考网文()。文章中,把执行代码集成在ShellUtils工具类中,执行结果返回CommandResult这个类。ShellUtils与CommandResult这两个类的说明可...

    利用Python+Java调用Shell脚本时的死锁陷阱详解

    主要给大家介绍了关于利用Python+Java调用Shell脚本时的死锁陷阱的相关资料,文章通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。

    JAVA上百实例源码以及开源项目

    1个目标文件,JNDI的使用例子,有源代码,可以下载参考,JNDI的使用,初始化Context,它是连接JNDI树的起始点,查找你要的对象,打印找到的对象,关闭Context…… ftp文件传输 2个目标文件,FTP的目标是:(1)提高...

    JAVA上百实例源码以及开源项目源代码

    Java从网络取得文件 1个目标文件 简单 Java从压缩包中提取文件 1个目标文件 简单 Java存储与读取对象 1个目标文件 如题 Java调色板面板源代码 1个目标文件 摘要:Java源码,窗体界面,调色板 使用Java语言编写的一款...

    hadoop+hive+mapreduce的java例子

    基于hadoop的Hive数据仓库JavaAPI简单调用的实例,关于Hive的简介在此不赘述。hive提供了三种用户接口:CLI,JDBC/ODBC和 WebUI CLI,即Shell命令行 JDBC/ODBC 是 Hive 的Java,与使用传统数据库JDBC的方式类似 Web...

    基于若依框架的Java学生心理健康信息管理系统设计源码

    这些文件详细展示了如何使用Java、HTML、JavaScript、CSS和Shell等技术构建一个面向学生和心理教师的心理健康信息管理系统,该系统支持学生在线进行心理测试、咨询心理教师、发帖讨论等功能,以及心理教师进行测试...

    JAVA自学之路

    这里只是讲了路线图,关于路线中的各个阶段,学到什么程度,如何学习等,可以参考后面的JAVA自学之路 七:《路线图明细》。 首先要学JavaSE,这是无庸置疑的。 与此同时,是的,与此同时,和JavaSE的学习同步,...

    使用iconv批量改变文件编码的shell脚本

    这篇文章主要介绍了编写shell脚本,使用iconv批量改变文件编码的脚本代码,需要的朋友可以参考下。 用法示例: cd ~/workspace/XXXProject ~/iconv_shell.sh ./ *java 好了,直接上代码~~ #!/bin/bash if [ $# != 2...

    java写dll接口源码-JNI-By-Examples::sparkler:有趣的JavaJNI示例-使用CMake和C++(当然也可以是C!):double_exclamation_mark:接受PR

    java写dll接口源码(介绍)Java 本机接口编程,通过示例 注意:我现在正在考虑向本教程添加内容。 如果您有建议或新增内容,请不要犹豫。 目录 前言 注意:我在 2017 年初写了这篇文章,因为需要将图像序列化任务从 ...

    开发基于Java的图形用户界面

    SWT(Standard Widget Toolkit)是IBM推出的“基于java”的图形界面开发库,我之所以说它是“基于java”的意思是程序员编写代码的时候是使用java语言,事实上SWT的底层实现是C语言完成的。但是这些对程序员是透明的。 ...

    SubEthaEdit-5.1.3.zip 可以写文章,代码,笔记

    无论他们在哪里,都可以写文章,代码,笔记或与朋友见面。极限编程会议,辅导和创意写作的理想选择。 强大的编辑 细颗粒还原 可以按照您的方式执行撤消操作,而不必将太多的编辑分组在一起。 块编辑 同时在多行...

    ADBKeyBoard:通过ADB的Android虚拟键盘输入(用于测试自动化)

    有一个shell命令“输入”,可以帮助您将文本输入发送到Android系统。 usage: input [text|keyevent] input text input keyevent 但是您不能使用此命令发送unicode字符,因为它并非旨在以这种方式使用它。 参考...

    基于vue-admin-template的hexo博客后台管理,项目采用SpringBoot+Vue+源代码+文档说明

    后端可以使用 java -jar hexoblogadmin.war 的方式直接运行, 或者部署到tomcat容器中 可以使用supervisor 或 systemd 托管springboot应用,避免退出shell后进程终止 ### TODO - 头像上传 - 系统参数设置 √ - ...

    UNIX教程网络篇

    本书可作为Unix用户学习的教程和参考书。 目 录 译者序 前言 第一部分 电子邮件 第1章 电子邮件概述 1 1.1 用户网络地址 1 1.1.1 Internet域寻址方式 2 1.1.2 UUCP路径寻址方式 3 1.2 发送和接收邮件 3 1.3 收到...

    本项目为量化开源课程可以帮助人们快速掌握量化金融知识以及使用Python进行量化开发的能力.zip

    3. 如果涉及到引用文章,请附上参考引用链接。 4. 当前进度 | 章节号 | 标题 | 进度 | 负责人&参与人 | | ------ | ------------------ | ------ | -------------------------------------- | | 第一章 | 投资与...

    操作系统(内存管理)

    在对内存块进行了 free 调用之后,我们需要做的是诸如将它们标记为未被使用的等事情,并且,在调用 malloc 时,我们要能够定位未被使用的内存块。因此, malloc 返回的每块内存的起始处首先要有这个结构: 清单 3...

    新版助理电子商务师模拟试题-(1).docx

    * CGI 程序可以使用 Perl、shell、C++、Java 等语言编写。 * CGI 程序的特点包括:内容的生成与显示进行别离、强调可重用的组件等。 知识点4:JSP * JSP 的特点包括:内容的生成与显示进行别离、强调可重用的组件...

    python数据分析随书代码

    Ivan Idris以编写简洁可测试的程序代码以及撰写有趣的技术文章为乐,同时也是Packt出版社NumPy Beginner's Guide-Second Edition、NumPy Cookbook和Learning NumPy Array等书籍的作者。读者可以访问ivanidris.net...

    049-冰蝎,从入门到魔改.pdf

    主要功能为:基本信息、命令执行、虚拟终端、文件管理、Socks 代理、反弹 shell、数据库管理、自定义代码等,功能非常强大。 加密原理 --------- 我们以 PHP 版本为例,"冰蝎" 在服务端支持 open_ssl 时,使⽤ AES...

Global site tag (gtag.js) - Google Analytics