Java 怎么操作Shell脚本

我们需要借助Java的Process类调用shell脚本处理一些任务!

下面我主要介绍两点:

  1. Java的Process类是什么

  2. 如何使用Process类操作Shell脚本指令

  3. process 类操作shell脚本文件

  4. Process类和ProcessBuilder源码

一、Java的 Process类

为了执行调用操作,JVM会启一个Process,Process类主要有以下几个方法:

public abstract class Process {

细心的读者会发现,为了执行调用操作,JVM会启一个Process,所以我们可以通过调用Process类的以下方法,得知调用操作是否正确执行:

1.1 waitFor() 

//导致当前线程等待//返回值是进程的出口值。0 表示正常终止;否则,就表示异常失败。abstract int waitFor()

1.2 isAlive() 

// 检测子进程是否存活,存活则返回 true public boolean isAlive()

另外,调用某些Shell命令或脚本时,会有返回值,那么我们如果捕获这些返回值或输出呢?为了解决这个问题,Process类提供了:

1.2 getInputStream() ​​​​​​​

//获取子进程的输入流。 最好对输入流进行缓冲。abstract InputStream getInputStream() 

二、如何使用Process类操作Shell脚本指令

主要实现方式有两种:

  • 直接使用 Process类处理进程

  • 使用ProcessBuilder构造一个进程生成器

2.1 直接使用 Process类操作

在linux系统查看系统当前目录

2.2 使用ProcessBuilder构造一个进程生成器

使用Java的ProcessBuilder实现同样的功能

2.3 Runtime方式和ProcessBuilder方式对比

  • Runtime方式:此为最常见的一种运行方式,历史最悠久,使应用程序能够与其运行的环境相连接,但是在读取上还存在一些不便性,正常的输出流与错误流得分开读取。其他功能基本相同。

  • ProcessBuilder:此为jdk1.5加入的,它没有将应用程序与其运行的环境相连接这个就需要自己设置其相关的信息但它提供了将正常流与流程流合并在一起的解决办法,只需要设置redirectErrorStream(错误流重定向到标准数据流)属性即可,这样更有利于脚本执行过程中出现问题时排查。

三、Process类操作Shell脚本文件

假如我要调用的Shell脚本是文件:/root/experiment/test.sh​​​​​​​

#!/usr/bin/env bashargs=1if [ $# -eq 1 ];then args=$1 echo "The argument is: $args"fi echo "This is a $call"start=`date +%s`sleep 3send=`date +%s`cost=$((($end - $start) * $args * $val))echo "Cost Time: $cost"

 

Java调用代码

public static void callShellScript_test() {    BufferedReader bufferedReader = null;    try {        String shellPath = CallShellScriptDemo01.class.getResource("/").getPath() + "shell/test.sh";        ProcessBuilder builder = new ProcessBuilder("/bin/sh", shellPath);        // 错误流重定向到标准输出流        builder.redirectErrorStream(true);        Process ps = builder.start();        int exitValue = ps.waitFor();        if (0 != exitValue) {            log.error("call shell failed. error code is :" + exitValue);        }        bufferedReader = new BufferedReader(new InputStreamReader(ps.getInputStream()));        String line;        while ((line = bufferedReader.readLine()) != null) {            System.out.println("line = " + line.toString());        }    } catch (Exception e) {        e.printStackTrace();    }    log.info("数据刷新完成");}

 

四、Process类和ProcessBuilder源码

4.1 Process类

源代码虽是英文的,但是可以通过对原注释的阅读,直接体会jdk原创作者的真实想法,这样大家对process这个类会有更深一层次的理解。

4.2 ProcessBuilder

public final class ProcessBuilder

 

/** 源码中的注释

This class is used to create operating system processes. 此类用于创建操作系统进程

Each {@code ProcessBuilder} instance manages a collection of process attributes.

The {@link #start()} method creates a new {@link Process} instance with those attributes.

The {@link
 * #start()} method can be invoked repeatedly--重复地 from the same instance
 * to create new subprocesses with identical or related attributes.

*/

Each process builder manages these process attributes: 每个进程生成器都管理这些进程属性: 

command</i>, a list of strings which signifies--表示... the
 * external--外部-额外 program file to be invoked and its arguments, if any.

Which string lists represent-代表,扮演 a valid--有效的 operating system command is
 * system-dependent. 

 * <li>an <i>environment</i>, which is a system-dependent mapping from
 * <i>variables</i> to <i>values</i>.  The initial value is a copy of
 * the environment of the current process (see {@link System#getenv()}).

 * <li>a <i>working directory</i>.  The default value is the current
 * working directory of the current process, usually the directory
 * named by the system property {@code user.dir}.

 

Note that this class is not synchronized.  -- 线程不安全

* If multiple threads access a {@code ProcessBuilder} instance
 * concurrently, and at least one of the threads modifies one of the
 * attributes structurally, it <i>must</i> be synchronized externally.

 

例子:
 * <p>Starting a new process which uses the default working directory
 * and environment is easy:

 * <pre> {@code
 * Process p = new ProcessBuilder("myCommand", "myArg").start();
 * }</pre>

下面是一个示例,它使用修改的工作目录和环境启动进程,并重定向标准输出和错误*以附加到日志文件中: <p>Here is an example that starts a process with a modified working
 * directory and environment, and redirects standard output and error
 * to be appended to a log file:

 ProcessBuilder pb =
 *   new ProcessBuilder("myCommand", "myArg1", "myArg2");
 * Map<String, String> env = pb.environment();
 * env.put("VAR1", "myValue");
 * env.remove("OTHERVAR");
 * env.put("VAR2", env.get("VAR1") + "suffix");
 * pb.directory(new File("myDir"));


 * File log = new File("log");
 * pb.redirectErrorStream(true);
 * pb.redirectOutput(Redirect.appendTo(log));
 * Process p = pb.start();
 * assert pb.redirectInput() == Redirect.PIPE;
 * assert pb.redirectOutput().file() == log;
 * assert p.getInputStream().read() == -1;

 * <p>To start a process with an explicit set of environment
 * variables, first call {@link java.util.Map#clear() Map.clear()}
 * before adding environment variables.

 

有了这个方向,相信大家会少踩一些坑,而且大胆地使用java和脚本之间的交互。java可以调用shell,那么shell再调用其他就方便了。不过, 记得一点,不要过度地依赖缓冲区进行线程之间的通信。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值