• Java 命令执行笔记


    依旧是跟着 feng 师傅的博客进行学习,过一遍feng师傅的笔记,再写到自己的博客上

    Runtime

    Runtime 类 ,就是运行时环境

    1. public class Runtime {
    2. private static final Runtime currentRuntime = new Runtime();
    3. private static Version version;
    4. public static Runtime getRuntime(){
    5. return currentRuntime;
    6. }
    7. private Runtime(){}
    8. }

    这种涉及模式叫做单例模式:

    1. 单例模式(Singleton)的目的是为了保证在一个进程中,某个类有且仅有一个实例。
    2. 因为这个类只有一个实例,因此,自然不能让调用方使用new Xyz()来创建实例了。所以,单例的构造方法必须是private,这样就防止了调用方自己创建实例,但是在类的内部,是可以用一个静态字段来引用唯一创建的实例的。
    3. 那么问题来了,外部调用方如何获得这个唯一实例?
    4. 答案是提供一个静态方法,直接返回实例:
    5. 或者直接把static变量暴露给外部:

    java 进程种 有一个 Runtime 类对象,这个对象通过 getRuntime 方法 获得

    1. private static final Runtime currentRuntime = new Runtime();
    2. public static Runtime getRuntime() {
    3. return currentRuntime;
    4. }

    exec方法的调用链

    命令执行时 就是使用的exec方法,该方法有如下几种重载

    1. public Process exec(String command)-----在单独的进程中执行指定的字符串命令。
    2. public Process exec(String [] cmdArray)---在单独的进程中执行指定命令和变量
    3. public Process exec(String command, String [] envp)----在指定环境的独立进程中执行指定命令和变量
    4. public Process exec(String [] cmdArray, String [] envp)----在指定环境的独立进程中执行指定的命令和变量
    5. public Process exec(String command,String[] envp,File dir)----在有指定环境和工作目录的独立进程中执行指定的字符串命令
    6. public Process exec(String[] cmdarray,String[] envp,File dir)----在指定环境和工作目录的独立进程中执行指定的命令和变量

    ctrl + B 进入  exec  跟进源码

    1. public Process exec(String command) throws IOException {
    2. return exec(command, null, null);
    3. }

    继续 跟进exec

    1. public Process exec(String command, String[] envp, File dir)
    2. throws IOException {
    3. if (command.isEmpty())
    4. throw new IllegalArgumentException("Empty command"); //判断是否为空
    5. StringTokenizer st = new StringTokenizer(command);
    6. String[] cmdarray = new String[st.countTokens()];
    7. for (int i = 0; st.hasMoreTokens(); i++)
    8. cmdarray[i] = st.nextToken();
    9. return exec(cmdarray, envp, dir);
    10. }

    继续跟进 exec

    1. public Process exec(String[] cmdarray, String[] envp, File dir)
    2. throws IOException {
    3. return new ProcessBuilder(cmdarray)
    4. .environment(envp)
    5. .directory(dir)
    6. .start();
    7. }

    跟进到这,返回了一个 ProcessBuilder,看这英文,是进程?继续跟进下面的.start()方法

    产生了一个 ProcessImpl 对象,调用了他的start方法,跟进:

     该方法返回了一个 ProcessImpl对象

    至此 链子返回,总体流程是(借用feng师傅的图):

     exec 返回值

    正常的命令执行:

    Runtime.getRuntime().exec("whoami");
    

    如果需要获得命令的执行结果的话,还需要处理,设置一个IO流缓冲区来读取:

    1. public class a {
    2. public static void main(String[] args) throws Exception{
    3. InputStream in = Runtime.getRuntime().exec("whoami").getInputStream();
    4. byte bcache [] = new byte[1024]; //设置缓冲区大小
    5. int readsize = 0;// 读取大小
    6. try(ByteArrayOutputStream outputStream = new ByteArrayOutputStream()){
    7. while ((readsize = in.read(bcache)) != -1){
    8. outputStream.write(bcache,0,readsize);
    9. }
    10. System.out.println(outputStream.toString()); //打印
    11. }}}

    exec 方法返回的的是一个 Process 对象,因此他有这些方法:

    1. destroy()
    2. 杀掉子进程。
    3. exitValue()
    4. 返回子进程的出口值。
    5. InputStream getErrorStream()
    6. 获得子进程的错误流。
    7. InputStream getInputStream()
    8. 获得子进程的输入流。
    9. OutputStream getOutputStream()
    10. 获得子进程的输出流。
    11. waitFor()
    12. 导致当前线程等待,如果必要,一直要等到由该 Process 对象表示的进程已经终止。

    通过反射类 来执行Runtime类的exec 方法

    通过getRuntime 静态方法来获得Runtime 对象

    1. public class a {
    2. public static void main(String[] args) throws Exception {
    3. //Runtime.getRuntime().exec("whoami");
    4. Class> clazz = Class.forName("java.lang.Runtime");
    5. Method execMethod = clazz.getMethod("exec", String.class);
    6. Method getRuntimeMethod = clazz.getMethod("getRuntime");
    7. Object runtime = getRuntimeMethod.invoke(Runtime.class);
    8. Process inProcess = (Process) execMethod.invoke(runtime, "whoami"); //需要类型转换
    9. //打印 exec命令返回结果
    10. InputStream in = inProcess.getInputStream();
    11. byte[] bcache = new byte[1024];
    12. int readSize = 0;
    13. try(ByteArrayOutputStream outputStream = new ByteArrayOutputStream()){
    14. while ((readSize =in.read(bcache))!=-1){
    15. outputStream.write(bcache,0,readSize);
    16. }
    17. System.out.println(outputStream.toString());}}}

    通过构造器获得Runtime 对象

    1. public class a {
    2. public static void main(String[] args) throws Exception {
    3. //Runtime.getRuntime().exec("whoami");
    4. Class> clazz = Class.forName("java.lang.Runtime");
    5. Constructor> cons = clazz.getDeclaredConstructor();
    6. cons.setAccessible(true);
    7. Object runtime = cons.newInstance();
    8. Method exec = clazz.getMethod("exec", String.class);
    9. Process inprocess = (Process) exec.invoke(runtime, "whoami");
    10. //打印返回结果
    11. InputStream in = inprocess.getInputStream();
    12. byte[] bcache = new byte[1024];
    13. int readSize = 0;
    14. try(ByteArrayOutputStream outputStream = new ByteArrayOutputStream()){
    15. while ((readSize =in.read(bcache))!=-1){
    16. outputStream.write(bcache,0,readSize);
    17. }
    18. System.out.println(outputStream.toString());
    19. }}}

    现在终于明白 该如何获得 通过这两者的方式获得 对象并调用了呜呜呜。。。这里需要注意的是,因为是单例模式,Runtime类的构造器是private 的,所以需要 setAccessible(true),而且需要用getDeclaredConstructorgetConstructor只能获得public的。

    复杂的命令执行

    exec 不能应付复杂的命令执行,参考文章:

    Java Runtime.getRuntime().exec由表及里 - 先知社区

    又是看底层,看不懂, 但一般的解决方法是换一个重载器的exec()

    public Process exec(String [] cmdArray)---在单独的进程中执行指定命令和变量
    

     以数组的形式存在:

    • 在windows 中 以 cmd /c 开头的执行
    • 在Linux 中 以 /bin/sh  -c 开头
    1. String[] command = { "/bin/sh", "-c", "echo 2333 2333 2333 && echo 2333 2333 2333" };
    2. InputStream in = Runtime.getRuntime().exec(command).getInputStream();

    ProcessBuilder

    因为 exec 的链子在途中 会调用 类的 Start() 方法

    1. public Process exec(String[] cmdarray, String[] envp, File dir)
    2. throws IOException {
    3. return new ProcessBuilder(cmdarray)
    4. .environment(envp)
    5. .directory(dir)
    6. .start();
    7. }

    因此 我们也可以通过 来实现命令执行,根据逻辑,先创建 ProcessBuilder 对象的时候传入的 参数是要执行的命令,然后调用start方法执行。

    ProcessBuilder 的构造器:

    1. public ProcessBuilder(String... command) {
    2. this.command = new ArrayList<>(command.length);
    3. for (String arg : command)
    4. this.command.add(arg);
    5. }
    6. public ProcessBuilder command(List command) {
    7. if (command == null)
    8. throw new NullPointerException();
    9. this.command = command;
    10. return this;
    11. }

    和exec 差不多 可以调用 String的 也可以传入数组。

    start 方法也是返回一个 Process对象 :

    1. public Process start() throws IOException {
    2. return start(redirects);
    3. }

    非反射命令执行

    1. InputStream in = new ProcessBuilder("whoami").start().getInputStream();
    2. byte[] bcache = new byte[1024];
    3. int readSize = 0;
    4. try(ByteArrayOutputStream out = new ByteArrayOutputStream()){
    5. while( (readSize = in.read(bcache)) != -1){
    6. out.write(bcache,0,readSize);
    7. }
    8. System.out.println(out.toString());
    9. }

    反射命令执行

    1. Class clazz = Class.forName("java.lang.ProcessBuilder");
    2. Constructor cons = clazz.getConstructor(String[].class);
    3. Method startMethod = clazz.getMethod("start");
    4. Object processBuilder = cons.newInstance(new String[][]{{"whoami"}});
    5. InputStream in = ((Process) startMethod.invoke(processBuilder)).getInputStream();
    6. byte[] bcache = new byte[1024];
    7. int readSize = 0;
    8. try(ByteArrayOutputStream out = new ByteArrayOutputStream()){
    9. while ( (readSize = in.read(bcache))!= -1){
    10. out.write(bcache,0,readSize);
    11. }
    12. System.out.println(out.toString());
    13. }

    此处:

     InputStream in = ((Process) startMethod.invoke(processBuilder)).getInputStream();

    因为 newinstance 可以接收一个可变长度的object ,而这个构造器传入的可以是一个可变长度的String,叠加起来是一个二维数组。

  • 相关阅读:
    MongoDB 遇见 spark(进行整合)
    Redis(5)----浅谈压缩列表
    MarkBERT: 一种通过简单添加词边界的方法来增强预训练模型
    视觉slam十四讲CH4 ---李群与李代数求导
    基于PHP+MySQL的图书分享平台
    IEJoin: 提高 Databend range join 性能
    Java-数据类型
    Spring源码级笔记(一)
    Windows&PowerShell安装配置Vim的折腾记录
    前端进击笔记第二十六节 大型前端项目的痛点和优化方案
  • 原文地址:https://blog.csdn.net/snowlyzz/article/details/127657661