Java Shell
在工作过程中,大部分是Java开发,但只会Java肯定远远不够。 在文本处理过程中,主要是用脚本进行开发。脚本开发的特点就是在进行批处理的时候非常方便。比如这么一个需求场景:
有一些特殊文件需要特殊环境才能操作(比如:IOS 奔溃文件还原,Android 奔溃混淆文件还原),而部署上线后必须依赖Linux环境执行,所以需要借助Java的Process类调用shell脚本处理一些任务!
下面主要介绍两点:

  1. Java的Process类是什么
  2. 如何使用Process类操作Shell脚本指令
  3. Process 类操作shell脚本文件
  4. Process类和ProcessBuilder源码

    一、Java中的 Process类

    为了执行调用操作,JVM会启一个Process,Process类主要有以下几个方法:
    image.png
    为了执行调用操作,JVM会启一个Process,所以可以通过调用Process类的以下方法,得知调用操作是否正确执行:

    1.1 waitFor()

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

    1.2 isAlive()

    1. // 检测子进程是否存活,存活则返回 true
    2. public boolean isAlive()
    另外,调用某些Shell命令或脚本时,会有返回值,那么如果捕获这些返回值或输出呢?为了解决这个问题,Process类提供了:

    1.3 getInputStream()

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

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

    主要实现方式有两种:
  • 直接使用 Process类处理进程
  • 使用ProcessBuilder构造一个进程生成器

    2.1 直接使用 Process类操作

    在Linux系统查看系统当前目录
    image.png
    使用Java调用实现同样的功能

    1. private static void execDemo() throws IOException, InterruptedException {
    2. Process process = Runtime.getRuntime().exec("pwd");
    3. // 检查执行状态
    4. int exitValue = process.waitFor();
    5. if (0 != exitValue) {
    6. log.error("call shell failed. error code is :" + exitValue);
    7. }
    8. BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
    9. String line = null;
    10. while ((line = reader.readLine()) != null) {
    11. System.out.println("line = " + line);
    12. }
    13. }

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

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

    1. private static void ProcessBuilderDemo() throws IOException, InterruptedException {
    2. ProcessBuilder processBuilder = new ProcessBuilder("pwd");
    3. // 将错误流重定向到输出流
    4. processBuilder.redirectErrorStream(true);
    5. Process start = processBuilder.start();
    6. int exitValue = start.waitFor();
    7. if (0 != exitValue) {
    8. log.error("call shell failed. error code is :" + exitValue);
    9. }
    10. BufferedReader reader = new BufferedReader(new InputStreamReader(start.getInputStream()));
    11. String line = null;
    12. while ((line = reader.readLine()) != null) {
    13. System.out.println("line = " + line);
    14. }
    15. }

    2.3 Runtime方式和ProcessBuilder方式对比

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

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

    三、Process类操作Shell脚本文件

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

    ```bash

    !/usr/bin/env bash

    args=1 if [ $# -eq 1 ];then args=$1 echo “The argument is: $args” fi

echo “This is a $call” start=date +%s sleep 3s end=date +%s cost=$((($end - $start) $args $val)) echo “Cost Time: $cost”

  1. <a name="Hhtfh"></a>
  2. ### Java调用代码
  3. ```java
  4. public static void callShellScript_test() {
  5. BufferedReader bufferedReader = null;
  6. try {
  7. String shellPath = CallShellScriptDemo01.class.getResource("/").getPath() + "shell/test.sh";
  8. ProcessBuilder builder = new ProcessBuilder("/bin/sh", shellPath);
  9. // 错误流重定向到标准输出流
  10. builder.redirectErrorStream(true);
  11. Process ps = builder.start();
  12. int exitValue = ps.waitFor();
  13. if (0 != exitValue) {
  14. log.error("call shell failed. error code is :" + exitValue);
  15. }
  16. bufferedReader = new BufferedReader(new InputStreamReader(ps.getInputStream()));
  17. String line;
  18. while ((line = bufferedReader.readLine()) != null) {
  19. System.out.println("line = " + line.toString());
  20. }
  21. } catch (Exception e) {
  22. e.printStackTrace();
  23. }
  24. log.info("数据刷新完成");
  25. }

四、Process类和ProcessBuilder源码

4.1 Process类

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

  1. /**
  2. * The {@link ProcessBuilder#start()} and
  3. * {@link Runtime#exec(String[],String[],File) Runtime.exec}
  4. * methods create a native process and return an instance of a
  5. * subclass of {@code Process} that can be used to control the process
  6. * and obtain information about it. The class {@code Process}
  7. * provides methods for performing input from the process, performing
  8. * output to the process, waiting for the process to complete,
  9. * checking the exit status of the process, and destroying (killing)
  10. * the process.
  11. *
  12. * <p>The methods that create processes may not work well for special
  13. * processes on certain native platforms, such as native windowing
  14. * processes, daemon processes, Win16/DOS processes on Microsoft
  15. * Windows, or shell scripts.
  16. *
  17. * <p>By default, the created subprocess does not have its own terminal
  18. * or console. All its standard I/O (i.e. stdin, stdout, stderr)
  19. * operations will be redirected to the parent process, where they can
  20. * be accessed via the streams obtained using the methods
  21. * {@link #getOutputStream()},
  22. * {@link #getInputStream()}, and
  23. * {@link #getErrorStream()}.
  24. * The parent process uses these streams to feed input to and get output
  25. * from the subprocess. Because some native platforms only provide
  26. * limited buffer size for standard input and output streams, failure
  27. * to promptly write the input stream or read the output stream of
  28. * the subprocess may cause the subprocess to block, or even deadlock.
  29. *
  30. * <p>Where desired, <a href="ProcessBuilder.html#redirect-input">
  31. * subprocess I/O can also be redirected</a>
  32. * using methods of the {@link ProcessBuilder} class.
  33. *
  34. * <p>The subprocess is not killed when there are no more references to
  35. * the {@code Process} object, but rather the subprocess
  36. * continues executing asynchronously.
  37. *
  38. * <p>There is no requirement that a process represented by a {@code
  39. * Process} object execute asynchronously or concurrently with respect
  40. * to the Java process that owns the {@code Process} object.
  41. *
  42. * <p>As of 1.5, {@link ProcessBuilder#start()} is the preferred way
  43. * to create a {@code Process}.
  44. *
  45. * @since JDK1.0
  46. */
  47. public abstract class Process {
  48. /**
  49. * Returns the output stream connected to the normal input of the
  50. * subprocess. Output to the stream is piped into the standard
  51. * input of the process represented by this {@code Process} object.
  52. *
  53. * <p>If the standard input of the subprocess has been redirected using
  54. * {@link ProcessBuilder#redirectInput(Redirect)
  55. * ProcessBuilder.redirectInput}
  56. * then this method will return a
  57. * <a href="ProcessBuilder.html#redirect-input">null output stream</a>.
  58. *
  59. * <p>Implementation note: It is a good idea for the returned
  60. * output stream to be buffered.
  61. *
  62. * @return the output stream connected to the normal input of the
  63. * subprocess
  64. */
  65. public abstract OutputStream getOutputStream();
  66. /**
  67. * Returns the input stream connected to the normal output of the
  68. * subprocess. The stream obtains data piped from the standard
  69. * output of the process represented by this {@code Process} object.
  70. *
  71. * <p>If the standard output of the subprocess has been redirected using
  72. * {@link ProcessBuilder#redirectOutput(Redirect)
  73. * ProcessBuilder.redirectOutput}
  74. * then this method will return a
  75. * <a href="ProcessBuilder.html#redirect-output">null input stream</a>.
  76. *
  77. * <p>Otherwise, if the standard error of the subprocess has been
  78. * redirected using
  79. * {@link ProcessBuilder#redirectErrorStream(boolean)
  80. * ProcessBuilder.redirectErrorStream}
  81. * then the input stream returned by this method will receive the
  82. * merged standard output and the standard error of the subprocess.
  83. *
  84. * <p>Implementation note: It is a good idea for the returned
  85. * input stream to be buffered.
  86. *
  87. * @return the input stream connected to the normal output of the
  88. * subprocess
  89. */
  90. public abstract InputStream getInputStream();
  91. /**
  92. * Returns the input stream connected to the error output of the
  93. * subprocess. The stream obtains data piped from the error output
  94. * of the process represented by this {@code Process} object.
  95. *
  96. * <p>If the standard error of the subprocess has been redirected using
  97. * {@link ProcessBuilder#redirectError(Redirect)
  98. * ProcessBuilder.redirectError} or
  99. * {@link ProcessBuilder#redirectErrorStream(boolean)
  100. * ProcessBuilder.redirectErrorStream}
  101. * then this method will return a
  102. * <a href="ProcessBuilder.html#redirect-output">null input stream</a>.
  103. *
  104. * <p>Implementation note: It is a good idea for the returned
  105. * input stream to be buffered.
  106. *
  107. * @return the input stream connected to the error output of
  108. * the subprocess
  109. */
  110. public abstract InputStream getErrorStream();
  111. /**
  112. * Causes the current thread to wait, if necessary, until the
  113. * process represented by this {@code Process} object has
  114. * terminated. This method returns immediately if the subprocess
  115. * has already terminated. If the subprocess has not yet
  116. * terminated, the calling thread will be blocked until the
  117. * subprocess exits.
  118. *
  119. * @return the exit value of the subprocess represented by this
  120. * {@code Process} object. By convention, the value
  121. * {@code 0} indicates normal termination.
  122. * @throws InterruptedException if the current thread is
  123. * {@linkplain Thread#interrupt() interrupted} by another
  124. * thread while it is waiting, then the wait is ended and
  125. * an {@link InterruptedException} is thrown.
  126. */
  127. public abstract int waitFor() throws InterruptedException;
  128. /**
  129. * Causes the current thread to wait, if necessary, until the
  130. * subprocess represented by this {@code Process} object has
  131. * terminated, or the specified waiting time elapses.
  132. *
  133. * <p>If the subprocess has already terminated then this method returns
  134. * immediately with the value {@code true}. If the process has not
  135. * terminated and the timeout value is less than, or equal to, zero, then
  136. * this method returns immediately with the value {@code false}.
  137. *
  138. * <p>The default implementation of this methods polls the {@code exitValue}
  139. * to check if the process has terminated. Concrete implementations of this
  140. * class are strongly encouraged to override this method with a more
  141. * efficient implementation.
  142. *
  143. * @param timeout the maximum time to wait
  144. * @param unit the time unit of the {@code timeout} argument
  145. * @return {@code true} if the subprocess has exited and {@code false} if
  146. * the waiting time elapsed before the subprocess has exited.
  147. * @throws InterruptedException if the current thread is interrupted
  148. * while waiting.
  149. * @throws NullPointerException if unit is null
  150. * @since 1.8
  151. */
  152. public boolean waitFor(long timeout, TimeUnit unit)()
  153. /**
  154. * Returns the exit value for the subprocess.
  155. *
  156. * @return the exit value of the subprocess represented by this
  157. * {@code Process} object. By convention, the value
  158. * {@code 0} indicates normal termination.
  159. * @throws IllegalThreadStateException if the subprocess represented
  160. * by this {@code Process} object has not yet terminated
  161. */
  162. public abstract int exitValue();
  163. /**
  164. * Kills the subprocess. Whether the subprocess represented by this
  165. * {@codeProcess} object is forcibly terminated or not is
  166. * implementation dependent.
  167. */
  168. public abstract void destroy();
  169. /**
  170. * Kills the subprocess. The subprocess represented by this
  171. * {@code Process} object is forcibly terminated.
  172. *
  173. * <p>The default implementation of this method invokes {@link #destroy}
  174. * and so may not forcibly terminate the process. Concrete implementations
  175. * of this class are strongly encouraged to override this method with a
  176. * compliant implementation. Invoking this method on {@code Process}
  177. * objects returned by {@link ProcessBuilder#start} and
  178. * {@link Runtime#exec} will forcibly terminate the process.
  179. *
  180. * <p>Note: The subprocess may not terminate immediately.
  181. * i.e. {@code isAlive()} may return true for a brief period
  182. * after {@code destroyForcibly()} is called. This method
  183. * may be chained to {@code waitFor()} if needed.
  184. *
  185. * @return the {@code Process} object representing the
  186. * subprocess to be forcibly destroyed.
  187. * @since 1.8
  188. */
  189. public Process destroyForcibly(){}
  190. /**
  191. * Tests whether the subprocess represented by this {@code Process} is
  192. * alive.
  193. *
  194. * @return {@code true} if the subprocess represented by this
  195. * {@code Process} object has not yet terminated.
  196. * @since 1.8
  197. */
  198. public boolean isAlive(){}
  199. }