1.使用Runtime.getRuntime().exec()方法

sshUtil.java

package com.demo;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.Session;
import ch.ethz.ssh2.StreamGobbler;

public class SSHUtil {
    private String host; // ip
    private String username;
    private String password;

    /**
     * @param host(ip)
     * @param username(用户名)
     * @param password(密码)
     */
    public SSHUtil(String host, String username, String password) {
        this.host = host;
        this.username = username;
        this.password = password;
    }

    private Connection conn = null;

    /**
     * 连接
     */
    private boolean connect() {
        conn = new Connection(host);
        try {
            conn.connect();
            if (conn.authenticateWithPassword(username, password)) {
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * 执行命令
     */
    public String execute(String command) {
        if (command == null || "null".equals(command)) {
            return "传入命令为空!!!";
        }
        if (!connect()) {
            return "连接服务器失败!!!";
        }
        Session session = null;
        try {
            session = conn.openSession();
            session.execCommand(command);
            InputStream stdout = new StreamGobbler(session.getStdout());
            BufferedReader br = new BufferedReader(new InputStreamReader(stdout));
            StringBuffer sb = new StringBuffer();

            // ExitCode正常为0
            // 1:报错、2:误用命令、126:命令不得执行、127:没找到命令、130:ctrl+c结束、255:返回码超出范围
            sb.append("ExitCode: " + session.getExitStatus() + "\n");
            while (true) {
                String line = br.readLine();
                if (line == null)
                    break;
                sb.append(line + "\n");
            }
            return sb.toString();
        } catch (IOException e) {
            e.printStackTrace();
            return "error";
        } finally {
            if (conn != null) {
                conn.close();
            }
            if (session != null) {
                session.close();
            }
        }
    }
}

SSHMain.java

package com.demo;

public class SSHMain {

    public static void main(String[] args) {
        SSHUtil sshUtil=new SSHUtil("1.117.23.51","root","xxxx");

        String c1="cd /";
        String e1 = sshUtil.execute(c1);
        System.out.println(e1);

        String c2="pwd";
        String e2 = sshUtil.execute(c2);
        System.out.println(e2);
        
    }
}

执行结果:

image-20210906093238611

命令执行之Runtime.getRuntime().exec()

在java中,RunTime.getRuntime().exec()实现了调用服务器命令脚本来执行功能需要。

用法:

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

举例:

1,打开windows下记事本

RunTime.getRuntime().exec(String command);

在windows下相当于直接调用 /开始/搜索程序和文件 的指令,比如

Runtime.getRuntime().exec("notepad.exe");  -------打开windows下记事本。

2,public Process exec(String [] cmdArray);

Linux下:

Runtime.getRuntime().exec(new String[]{"/bin/sh","-c", cmds});

Windows下:

Runtime.getRuntime().exec(new String[]{ "cmd", "/c", cmds});

实例:

String command = "find " + source.getRoute() + " -name '" +source.getName();  

Process process = Runtime.getRuntime().exec(new String[] {"/bin/sh","-c",command});

补充:#!/bin/bash和#!/bin/sh的区别

#! 是个指示路径的表示符,/bin/bash和/bin/sh指定了脚本解析器的程序路径

bash是sh的完整版,bash完全兼容sh命令,反之不行

OPTIONS:

-c string    该选项表明string中包含了一条命令.如 bash -c ls ~

-i       使Bash以交互式方式运行

-r       使Bash以受限方式运行

–login     使Bash以登录Shell方式运行

–posix     使Bash遵循POSIX标准

–verbose    使Bash显示所有其读入的输入行

–help     打印Bash的使用信息

–version    打印版本信息

深入:

Process的几种用法:

1.destroy();杀掉子进程

2.exitValue();返回子进程的出口值,值0表示正常终止

3.getErrorStream();获取自己进程错误输出的输入流

4.getInputStream();这里是获取子进程输出的输入流

5.getOutputStream();获取子进程输入的输出流

6.waitFor();导致当前线程等待,如有必要,一直要等到由该Process对象表示的进程已经终止,如果已终止该子进程,此方法立即返回,如果没有终止该子进程,调用的线程将被阻塞,直到推出子进程,根据惯例,0表示正常终止

这里可能解释的不够清楚,首先子进程是指我们使用exec方法执行的进程,以getInputStream()方法为例,子进程的输出对于我们程序的进程是输入,所有相对java程序而言是输入流。

注意:在java中,调用runtime线程执行脚本是非常消耗资源的,所以切忌不要频繁使用!

在调用runtime去执行脚本的时候,其实就是JVM开了一个子线程去调用JVM所在系统的命令,其中开了三个通道: 输入流、输出流、错误流,其中输出流就是子线程走调用的通道。

大家都知道,waitFor是等待子线程执行命令结束后才执行, 但是在runtime中,打开程序的命令如果不关闭,就不算子线程结束。比如以下代码。

​ 代码:

​ private static Process p = null;

​ p = Runtime.getRuntime().exec(“notepad.exe”);

​ p.waitFor();

​ System.out.println("--------------------------------------------我被执行了");

​ 以上代码中,打开windows中记事本。如果我们不手动关闭记事本,那么输出语句就不会被执行,这点是需要理解的。

process的阻塞:

在runtime执行大点的命令中,输入流和错误流会不断有流进入存储在JVM的缓冲区中,如果缓冲区的流不被读取被填满时,就会造成runtime的阻塞。所以在进行比如:大文件复制等的操作时,我们还需要不断的去读取JVM中的缓冲区的流,来防止Runtime的死锁阻塞。

Java Runtime.exec()的使用

windows下调用程序

Process proc =Runtime.getRuntime().exec("exefile");

Linux下调用程序

Process proc =Runtime.getRuntime().exec("./exefile");

windows下调用程序系统命令

String [] cmd={"cmd","/C","copy exe1 exe2"}; 
Process proc =Runtime.getRuntime().exec(cmd);

Linux下调用系统命令就要改成下面的格式

String [] cmd={"/bin/sh","-c","ln -s exe1 exe2"}; 
Process proc =Runtime.getRuntime().exec(cmd);

windows下调用系统命令并弹出命令行窗口

String [] cmd={"cmd","/C","start copy exe1 exe2"}; 
Process proc =Runtime.getRuntime().exec(cmd);

Linux下调用系统命令并弹出终端窗口

String [] cmd={"/bin/sh","-c","xterm -e ln -s exe1 exe2"};
Process proc =Runtime.getRuntime().exec(cmd);

还有要设置调用程序的工作目录就要

Process proc =Runtime.getRuntime().exec("exeflie",null, new File("workpath"));

最好的执行命令的方法就是写个bat文件或者shell脚本,然后调用,那样修改和实现就简单多了

Java现在执行外部命令,主要的方式,还是通过调用平台的shell去完成,windows下就用cmd,Linux或者unix下面就用shell,下面演示对一个bat文件的调用,并把结果回显到控制台上。

读取bat配置文件

1.准备bat文件

image-20210906140424735

2,编写代码

package com.ssh;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;

public class JavaExeBat {
    public static void main(String[] args) {
        Process process;
        String cmd="D:\\Demo03\\test.bat";
        try {
            //执行命令
            process = Runtime.getRuntime().exec(cmd);
            //取得命令结果的输出流
            InputStream inputStream = process.getInputStream();
            //用一个读输出流类去读
            InputStreamReader isr = new InputStreamReader(inputStream, Charset.forName("GBK"));
            //用缓冲器读行
            BufferedReader br = new BufferedReader(isr);
            String line=null;
            //知道读完为止
            while ((line=br.readLine())!=null){
                System.out.println(line);
            }

        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

3.执行结果

image-20210906140645866

使用cmd /C

package com.ssh;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;

public class JavaExeBat {
    public static void main(String[] args) {
        Process process;
        //String cmd="D:\\Demo03\\test.bat";
        //String cmd="dir ";
        //String [] cmd={"cmd","/C","start copy exe1.txt exe4.txt"};
        String [] cmd={"cmd","/C","dir "};
        try {
            //执行命令
            process = Runtime.getRuntime().exec(cmd);
            //取得命令结果的输出流
            InputStream inputStream = process.getInputStream();
            //用一个读输出流类去读
            InputStreamReader isr = new InputStreamReader(inputStream, Charset.forName("GBK"));
            //用缓冲器读行
            BufferedReader br = new BufferedReader(isr);
            String line=null;
            //结果输出流
            //直到读完为止
            System.out.println("输出");
            while ((line=br.readLine())!=null){
                System.out.println(line);
            }

            //错误使出流
            System.out.println("错误");
            //输出流
            InputStream errorStream = process.getErrorStream();
            InputStreamReader error = new InputStreamReader(errorStream, Charset.forName("GBK"));
            BufferedReader erbr=new BufferedReader(error);
            line=null;
            while ((line=erbr.readLine())!=null){
                System.out.println(line);
            }


        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

image-20210906145255530

使用多线程的方式读取cmd命令,并把结果输出

package com.ssh;

import sun.rmi.runtime.Log;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.Charset;

public class javaExec {

    public static void main(String[] args) {
        //String[] cmds = new String("cmd", "/c", "dir ");
        String[] cmd = {"cmd", "/C", "dir "};
        runTimeExecutor(cmd);

    }
    public static void runTimeExecutor(String[] cmd){
        Runtime runtime=Runtime.getRuntime();
        try {
            Process process = runtime.exec(cmd);
            InputStream errorStream = process.getErrorStream();
            InputStream inputStream = process.getInputStream();
            readStreamInfo(errorStream,inputStream);
            int i = process.waitFor();
            process.destroy();
            if(i==0){
                System.out.println("子进程正常完成");
            }else{
                System.out.println("子进程异常结束");
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    /**
     * 读取Runtime.exec运行子进程的输入流
     */
    public synchronized static void readStreamInfo(InputStream... inputStreams){

        Object o=new Object();
        for(InputStream in:inputStreams){
            new Thread(()->{

                synchronized (o){
                    System.out.println("start");

                    try {
                        BufferedReader br = new BufferedReader(new InputStreamReader(in, Charset.forName("GBK")));
                        String line=null;
                        while ((line=br.readLine())!=null){
                            System.out.println(line);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }finally {
                        try {
                            in.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }

                    System.out.println("end");
                }

            }).start();
        }
    }
}

image-20210906164532304

StreamGobbler解释

StreamGobbler 是一个 InputStream,它使用内部工作线程不断地使用来自另一个 InputStream 的输入。它使用缓冲区来存储消耗的数据。如果需要,缓冲区大小会自动调整。 这个类有时非常方便——如果你用这个类的实例包装会话的 STDOUT 和 STDERR InputStreams,那么你就不必担心低级 SSH-2 协议中 STDOUT 和 STDERR 的共享窗口,因为所有到达数据将立即被工作线程消耗。此外,作为副作用,流将被缓冲(例如,单字节 read() 操作更快)。 其他用于 Java 库的 SSH 在其 STDOUT 和 STDERR InputStream 实现中默认包含此功能,但是,请注意这种方法也有一个缺点: 如果你调用StreamGobbler的read()方法不够频繁,对端不断发送大量数据,那么你迟早会遇到由于聚合数据导致的内存不足的情况(嗯,这也取决于Java堆尺寸)。无论如何,Joe Average 会喜欢这个类——一个偏执的程序员永远不会使用这种方法。

Logo

华为云1024程序员节送福利,参与活动赢单人4000元礼包,更有热门技术干货免费学习

更多推荐