精华内容
下载资源
问答
  • Expect4J1.4源码

    2013-01-06 23:06:46
    Expect4J1.4源码
  • expect4j使用体会心得

    千次阅读 2018-06-21 10:28:55
    下面讲一下如何应用expect4j来实现这些功能。 expect4j同样也是基于ssh协议的,他的底层依赖于jsch包,其实本质上他就是对jsch创建的通道流做处理,事先设定一个closure,当匹配到就做下一步操作,来达到自动交互...

        在实际的生产项目中,往往涉及很多的人机交互,这个时候想要程序自主完成一些动作,就显得比较麻烦,比如输入登录密码或者和一些堡垒机做交互。下面讲一下如何应用expect4j来实现这些功能。

        expect4j同样也是基于ssh协议的,他的底层依赖于jsch包,其实本质上他就是对jsch创建的通道流做处理,事先设定一个closure,当匹配到就做下一步操作,来达到自动交互完成一系类指令的功能。下面直接看代码,首先创建session

    private static Session getSession(String usr,String ip,String pwd) throws JSchException {
    		Session session = null;
    		try {
    			JSch jsch = new JSch();
    			session = jsch.getSession(usr, ip, 22);
    			Properties config = new Properties();
    			config.put("StrictHostKeyChecking", "no");
    			session.setConfig(config);
    			session.setPassword(pwd);
    			session.connect(3000);
    		} catch (JSchException e) {
    			if (session != null) {
    				session.disconnect();
    				session = null;
    			}
    			throw e;
    
    		}
    		return session;
    	}

        然后利用session打开一个shell channel,然后将channel流提供给expect4j,创建一个expect客户端

    ChannelShell channel = (ChannelShell) session.openChannel("shell");
    Expect4j expect = new Expect4j(channel.getInputStream(), channel.getOutputStream());
    channel.connect(3000);

        下面就是重头戏了,怎么让expect知道我上一条指令是否执行完毕。这个时候我们需要定义一些终止符,来提示表示可以执行下一条指令。下面看代码

    	public static List<Match> getPattern(List<String> promptRegEx, StringBuffer buffer)
    			throws MalformedPatternException {
    		List<Match> lstPattern = new ArrayList<Match>();
    		synchronized (promptRegEx) {
    			for (String regexElement : promptRegEx) {
    				RegExpMatch mat = new RegExpMatch(regexElement, x -> {
    					buffer.append(x.getBuffer());
    					// x.exp_continue();
    				});
    				lstPattern.add(mat);
    			}
    		}
    		lstPattern.add(new TimeoutMatch(DEFAULTTIMEOUT, x -> {
    		}));
    		return lstPattern;
    	}        

        上面函数中的promptRegEx即是自定义的终止符,一般就是操作系统的提示符,比如~或者$。非常重要的一点是,特殊字符必须进行转义,比如$,一定要写成\$,当然在JAVA中需要两个反斜杠来转义。buffer就是交互期间所有的操作以及结果记录,最后通过buffer.toString可以返回所有的结果。程序中我注释了一行 x.exp_continue(),这个表示即使匹配到了终止符,他也会继续往下逐个匹配,我个人觉得这个用处不大,而且非常影响性能,所以注释掉了,但是网上其他很多地方都加上了这句。

        另外一点,如果指令执行期间遇到网络问题或者其他一些未知问题导致卡住怎么办,可以看到我在程序中额外定义了一个超时的closure即TimeoutMatch,这个超时时间可以自己设定。

        下面就是具体的交互指令执行啦,样例程序如下

    List<Match> lstPattern = getPattern(promptRegEx, buffer);
    
    expect.expect(lstPattern);
    expect.send(cmd1);
    expect.expect(lstPattern);
    expect.send(cmd2);
    expect.expect(lstPattern);

        expect函数就是寻找终止符的操作,他返回一个int结果,表示第几次找到匹配的结果,或者返回一个错误码。send函数就是发送指令进行执行了,一定要记得在指令后面加上换行符,表示执行,要不然不会执行的。

        最后就是获取执行结果了,直接对上面的buffer进行toString就可以了。需要注意的是,如果远程机器返回结果有中文,且用的编码和当前主机环境变量使用编码不一致,这个时候就会出现乱码。解决办法就是,修改当前主机默认编码,如果实在不好修改,可以修改expect4j源码进行更改,他的源码中使用的编码就是获取的主机默认编码,其实可以手动自己设置。比如:

    /** Creates a new instance of ReaderConsumer */
    	public StreamPair(InputStream is, OutputStream os) {
    		try {
    			this.is = new InputStreamReader(is, "GBK");
    			this.os = new OutputStreamWriter(os, "GBK");
    		} catch (UnsupportedEncodingException e) {
    			e.printStackTrace();
    		}
    	}



    展开全文
  • expect4j.jar 1.0

    2011-08-23 14:21:26
    Expect4j is an attempt to rewrite Expect in Java and provide bindings to the TclJava interpreter. The goal is to be able to port existing Tcl code bases that use Expect directly to Java without ...
  • 使用expect4j完成一个交互式处理的java工具 最近要管理很多网络设备由于配置都差不多配什么静态路由啊,黑名单,等等。本来利用python的ansible可以对linux服务器进行这样的操作的但是这种网络设备完全不适用,他们...

    使用expect4j完成一个交互式处理的java工具

    最近要管理很多网络设备由于配置都差不多配什么静态路由啊,黑名单,等等。本来利用python的ansible可以对linux服务器进行这样的操作的但是这种网络设备完全不适用,他们无法执行基本的bash命令。故而只能使用基于命令行操作的方式来处理。程序的作用就是降低重复大量的机械劳动。

    介绍下expect的基本原理通过ssh链接读取字节流输出字节流,然后进行匹配看是否完成常用的是正则匹配、超时匹配、EOF匹配等,目前我用的主要是正则和超时,匹配过后可以针对读取的字节流进行操作,将他们保存在一个buffer当中。

    接下来开始结构说明

    //实例化一个ssh链接        
    session = new JSch().getSession(USERNAME, HOST_IP, HOST_PORT);
    //输入密码        
    session.setPassword(passwd);
    //链接的配置        
    session.setConfig("StrictHostKeyChecking", "no");
    //建立ssh链接        
    session.connect();
    //打开一个终端
    channel = (ChannelShell) session.openChannel("shell");
    //建立一个expect的输入输出流实例        
    expect = new Expect4j(channel.getInputStream(), channel.getOutputStream());
    //设置expect的默认超时时间(毫秒)        
    expect.setDefaultTimeout(2000);
    //终端链接 设定一个超时时间        
    channel.connect(sshTimeout + 500);

    这样我们就使用ssh连接到设备的终端上进而可以开始输入命令每个命令后面必带一个回车

    public static final String BACKSLASH_R = "\r";//回车
    expect.send(String.format("ssh %s %s", ip, HOST_PORT));
    expect.send(BACKSLASH_R);

    这样我们就能把命令输出出去了,但是如果我们想要看结果就需要使用expect.expect方法

    这里使用的是<作为结束如果输出流遇到<就会将他分割,后面是lambda对他的操作每次操作过后expect里面的内置buffer会从匹配的地方截断只有后面的,这样buffer里面就存入了输出数据了

    StringBuffer buffer = new StringBuffer();
    //判断是否结束
    expect.expect("<", state -> buffer.append(state.getBuffer()));

    对于经常要分页输出空格的比如linux的more命令之后要不停的按空格之类的我们可以采用这样的方法

    这里解释下COMMAND_EXECUTION_TIMEOUT 这个-2的意思,其实expect是一个多态里面最终运行的是一个Match接口的列表

    当我们匹配到列表顺序的Match之后就会返回他的值,这里我只有一个所以程序匹配超时时间的时候是-2 如果你是自己写了一个list那么要注意这里的顺序

    private static final int COMMAND_EXECUTION_TIMEOUT = -2;
    public static final String MORE_N = " ";//输出空格
    while (expect.expect("---- More ----", x -> buffer.append(x.getBuffer())) != COMMAND_EXECUTION_TIMEOUT) {
        expect.send(MORE_N);
    }

    这样“----- More ------”就会被跳过,整个输出也会出现

    最后只要再加上之前判断是否结束的标志就能完成整个输出了

    最后有些ping命令之类耗时较长的会因为默认10秒超时导致没有输出这里我们可以自己定义一个超时时间

    expect.setDefaultTimeout(timeout);
    

    贡献2个网络设备常用的方法方便大家使用

    private static final int COMMAND_EXECUTION_TIMEOUT = -2;
    private static final int COMMAND_EXECUTION_SUCCESS_OPCODE = 0;
    public static final String BACKSLASH_R = "\r";
    public static final String MORE_N = " ";
    
    public String executeCommand(String command) throws Exception {
            StringBuffer buffer = new StringBuffer();
            //清空之前发送的信息,并发送命令
            if (expect.expect(">", state -> buffer.setLength(0)) != COMMAND_EXECUTION_TIMEOUT) {
                expect.send(command);
                expect.send(BACKSLASH_R);
            }
    
    //                处理more需要空格才能显示完毕的问题
            while (expect.expect("---- More ----", x -> buffer.append(x.getBuffer())) != COMMAND_EXECUTION_TIMEOUT) {
                expect.send(MORE_N);
            }
            //判断是否结束
            expect.expect("<", state -> buffer.append(state.getBuffer()));
            //去掉命令行 和最后一个<
            return buffer.length() > 0 ? buffer.deleteCharAt(buffer.length() - 1).substring(command.length()) : "没有输出";
        }
    
        public void executeCommands(List<String> commands) throws Exception {
            expect.getLastState().setBuffer(null);
            //清空之前发送的信息,并发送命令
            expect.expect(">");
            expect.send("sys");
            expect.send(BACKSLASH_R);
            if (expect.expect("]") != COMMAND_EXECUTION_TIMEOUT) {
                for (String command : commands) {
                    expect.send(command);
                    expect.send(BACKSLASH_R);
    
                }
            }
            expect.expect("]");
            String buffer = expect.getLastState().getBuffer();
            if (buffer.length() > 0)
                System.out.println(buffer.substring(0, buffer.length() - 1));
        }

     

    展开全文
  • Java使用jsch和expect4j执行Linux命令

    千次阅读 2018-08-02 23:05:06
    import expect4j.Expect4j; import expect4j.matches.EofMatch; import expect4j.matches.Match; import expect4j.matches.RegExpMatch; import expect4j.matches.TimeoutMatch; import org.apache.oro.text.regex....

    1.背景

        最近项目上需要远程链接至Linxu服务器执行语句,所以自己研究了下,找到了几个开源框架,并且稍微封装了一个Util类,供大家参考。

    2.准备工作

        Pom文件引入依赖

    ​<dependency>
        <groupId>com.jcraft</groupId>
        <artifactId>jsch</artifactId>
        <version>0.1.54</version>
    </dependency>
    <dependency>
        <groupId>com.github.cverges</groupId>
        <artifactId>expect4j</artifactId>
        <version>1.9</version>
    </dependency>

    3.工具类分享

    import com.jcraft.jsch.ChannelShell;
    import com.jcraft.jsch.JSch;
    import com.jcraft.jsch.Session;
    import expect4j.Expect4j;
    import expect4j.matches.EofMatch;
    import expect4j.matches.Match;
    import expect4j.matches.RegExpMatch;
    import expect4j.matches.TimeoutMatch;
    import org.apache.oro.text.regex.MalformedPatternException;
    import org.springframework.stereotype.Component;
    
    import java.util.ArrayList;
    import java.util.Hashtable;
    import java.util.List;
    
    /**
     * 执行shell语句工具类
     */
    @Component
    public class CMDUtilShell {
        private static final int COMMAND_EXECUTION_SUCCESS_OPCODE = -2;
        private static final String BACKSLASH_R = "\r";
    
        /**
         * 执行配置命令
         * @param commands 要执行的命令,为字符数组
         * @return 执行是否成功
         */
        public String executeCommands(String[] commands, String ip, int port, String user, String password, Integer sshTimeout) {
            Session session = null;
            ChannelShell channel = null;
            Expect4j expect = null;
    
            try {
                session = new JSch().getSession(user, ip, port);
                session.setPassword(password);
                session.setConfig(bulidConfig());
                // 设置不提示输入密码、不显示登入信息等
                session.setUserInfo(new LocalUserInfo());
                session.connect();
                channel = (ChannelShell) session.openChannel("shell");
                expect = new Expect4j(channel.getInputStream(), channel.getOutputStream());
                channel.connect(sshTimeout + 1000);
    
                // 存放返回值
                StringBuffer buffer = new StringBuffer();
                // 执行语句
                List<Match> lstPattern = bulidPattern(buffer, sshTimeout);
    
                for (String strCmd : commands) {
                    if (expect.expect(lstPattern) != COMMAND_EXECUTION_SUCCESS_OPCODE) {
                        expect.send(strCmd);
                        expect.send(BACKSLASH_R);
                    }
                }
                expect.expect(lstPattern);
                return buffer.toString().toLowerCase();
    
            } catch (Exception ex) {
                String errorMsg = String.format("[@CMDUtilShell] host=[%s] user=[%s] [error=%s]", ip, user, ex.getMessage());
            } finally {
                if (expect != null) {
                    expect.close();
                }
                if (channel != null) {
                    channel.disconnect();
                }
                if (session != null) {
                    session.disconnect();
                }
            }
        }
    
        /**
         * 构建配置项
         */
        private Hashtable<String, String> bulidConfig() {
            Hashtable<String, String> config = new Hashtable<>();
            config.put("userauth.gssapi-with-mic", "no");
            config.put("StrictHostKeyChecking", "no");
            return config;
        }
    
        /**
         * 构建模式
         */
        private List<Match> bulidPattern(StringBuffer buffer, Integer sshTimeout) throws MalformedPatternException {
            List<Match> lstPattern = new ArrayList<>();
            // 终止符
            // todo:存入数据库使用参数配置
            String[] regEx = {"~]#", "~#", ":~#",};
            for (String regexElement : regEx) {
                RegExpMatch mat = new RegExpMatch(regexElement, x -> {
                    buffer.append(x.getBuffer());
                });
                lstPattern.add(mat);
            }
    
            lstPattern.add(new EofMatch(x -> {
            }));
    
            // 设置超时时间
            lstPattern.add(new TimeoutMatch(sshTimeout, x -> {
            }));
    
            return lstPattern;
        }
    }

     

    展开全文
  • expect4j 示例) expect() 方法可以处理正则表达式 Pattern 和文字字符串的混合:实际上它接受任意数量的 Object,然后使用 Pattern 作为正则表达式并使用 String 作为文字字符串。 Expect 一次不期望多个连接(这是...
  • expect

    2014-11-19 13:46:38
    expect用法 1. [#!/usr/bin/expect]  这一行告诉操作系统脚本里的代码使用那一个shell来执行。这里的expect其实和linux下的bash、windows下的cmd是一类东西。  注意:这一行需要在脚本的第一行。  2. ...
    

    expect用法

    1. [#!/usr/bin/expect] 

    这一行告诉操作系统脚本里的代码使用那一个shell来执行。这里的expect其实和linux下的bash、windows下的cmd是一类东西。 

    注意:这一行需要在脚本的第一行。 

    2. [set timeout 30] 

    基本上认识英文的都知道这是设置超时时间的,现在你只要记住他的计时单位是:秒   。timeout -1 为永不超时

    3. [spawn ssh -l username 192.168.1.1] 

    spawn是进入expect环境后才可以执行的expect内部命令,如果没有装expect或者直接在默认的SHELL下执行是找不到spawn命令的。所以不要用 “which spawn“之类的命令去找spawn命令。好比windows里的dir就是一个内部命令,这个命令由shell自带,你无法找到一个dir.com 或 dir.exe 的可执行文件。 

    它主要的功能是给ssh运行进程加个壳,用来传递交互指令。 

    4. [expect "password:"] 

    这里的expect也是expect的一个内部命令,有点晕吧,expect的shell命令和内部命令是一样的,但不是一个功能,习惯就好了。这个命令的意思是判断上次输出结果里是否包含“password:”的字符串,如果有则立即返回,否则就等待一段时间后返回,这里等待时长就是前面设置的30秒 

    5. [send "ispass\r"] 

    这里就是执行交互动作,与手工输入密码的动作等效。 

    温馨提示: 命令字符串结尾别忘记加上“\r”,如果出现异常等待的状态可以核查一下。 

    6. [interact] 

    执行完成后保持交互状态,把控制权交给控制台,这个时候就可以手工操作了。如果没有这一句登录完成后会退出,而不是留在远程终端上。如果你只是登录过去执行 

    7.$argv 参数数组

    expect脚本可以接受从bash传递过来的参数.可以使用[lindex $argv n]获得,n从0开始,分别表示第一个,第二个,第三个....参数

     

    下面的expect脚本的例子

    执行这个文件./launch.exp 1 2 3

    屏幕上就会分别打印出参数

    send_user用来发送内容给用户。

     

    参数运用方面还有很多技巧

    比如$argc 存储了参数个数,args被结构化成一个列表存在argv。$argv0 被初始化为脚本名字。

    除此之外,如果你在第一行(#!那行)使用-d (debug参数),可以在运行的时候输出一些很有用的信息

    比如你会看见

     

    argv[0] = /usr/bin/expect argv[1] = -d argv[2] = ./launch.exp argv[3] = 1 argv[4] = 2 argv[5] = 3

    使用这些也可以完成参数传递

    8.

    expect的命令行参数参考了c语言的,与bash shell有点不一样。其中,$argc为命令行参数的个数,$argv0为脚本名字本身,$argv为命令行参数。[lrange $argv 0 0]表示第1个参数,[lrange $argv 0 4]为第一个到第五个参数。与c语言不一样的地方在于,$argv不包含脚本名字本身。

     

    9.

    exp_continue的用法

    #!/usr/bin/expect -f

    set ipaddr "localhost"

    set passwd "iforgot"

    spawn ssh root@$ipaddr              #spawn   意思是执行命令,expect内命令,shell中不存在

    expect {

    "yes/no" { send "yes\r"; exp_continue}

    "password:" { send "$passwd\r" }

    }

    expect "]# "

    send "touch a.txt\r"                       #意思为发送命令

    send "exit\r"

    expect eof

    exit

     

    exp_continue可以继续执行下面的匹配,简单了许多。还有一点,让我认识到匹配不见得要匹配最后几个字符。

     

    10.拿来小例子   

    设置变量     set PASSWD   abcd123

    #!/usr/bin/expect -f

    # Expect script to supply root/admin password for remote ssh server

    # and execute command.

    # This script needs three argument to(s) connect to remote server:

    # password = Password of remote UNIX server, for root user.

    # ipaddr = IP Addreess of remote UNIX server, no hostname

    # scriptname = Path to remote script which will execute on remote server

    # If you username and passwd has not pass the rsa trust, your login will fail.

    # Usage For example:

    #  ./sshlogin.exp password 192.168.1.11 who

    # ------------------------------------------------------------------------

    # Copyright (c) 2004 nixCraft project <http://cyberciti.biz/fb/>

    # This script is licensed under GNU GPL version 2.0 or above

    # -------------------------------------------------------------------------

    # This script is part of nixCraft shell script collection (NSSC)

    # Visit http://bash.cyberciti.biz/ for more information.

    # ----------------------------------------------------------------------

    # set Variables

    set password [lrange $argv 0 0]

    set ipaddr [lrange $argv 1 1]

    set scriptname [lrange $argv 2 2]

    set arg1 [lrange $argv 3 3]

    set timeout -1

    # now connect to remote UNIX box (ipaddr) with given script to execute

    spawn ssh yourusername@$ipaddr $scriptname $arg1

    match_max 100000

    # Look for passwod prompt

    expect "*?assword:*"

    # Send password aka $password

    send -- "$password\r"

    # send blank line (\r) to make sure we get back to gui

    send -- "\r"

    expect eof

     

    ==============================================================================

     

    #!/usr/bin/expect 

     

     # 设置超时时间为 60 秒

     set timeout  60                                         

     # 设置要登录的主机 IP 地址

     set host 192.168.1.46

     # 设置以什么名字的用户登录

     set name root 

     # 设置用户名的登录密码

     set password 123456 

     

     #spawn 一个 ssh 登录进程

     spawn  ssh $host -l $name 

     # 等待响应,第一次登录往往会提示是否永久保存 RSA 到本机的 know hosts 列表中;等到回答后,在提示输出密码;之后就直接提示输入密码

     expect { 

        "(yes/no)?" { 

            send "yes\n"

            expect "assword:"

            send "$pasword\n"

        } 

            "assword:" { 

            send "$password\n"

        } 

     } 

     expect "#"

     # 下面测试是否登录到 $host 

     send "uname\n"

     expect "Linux"

     send_user  "Now you can do some operation on this terminal\n"

     # 这里使用了 interact 命令,使执行完程序后,用户可以在 $host 终端进行交互操作。

     Interact 

     

     

    ==============================================================================

    用expect实现ssh自动登录对服务器进行批量管理

     

    1.实现ssh自动登录完成任务的expect脚本

    #!/usr/bin/expect -f

    set ipaddress [lindex $argv 0]

    set passwd [lindex $argv 1]

    set timeout 30

    spawn ssh shellqun@$ipaddress

    expect {

    "yes/no" { send "yes\r";exp_continue }

    "password:" { send "$passwd\r" }

    }

    expect "*from*"

    send "mkdir -p ./tmp/testfile\r"

    #send "exit\r"

    expect "#"  命令运行完, 你要期待一个结果, 结果就是返回shell提示符了(是# 或者$)

    #最后一句第13行的解释:

     

    其实写成 interact 的最大好处是登录后不会退出,而会一直保持会话连接,可以后续手动处理其它任务,请根据实际情况自行选择了。

     

    2.调用login.exp完成批量管理

    #!/bin/bash

    for i in `awk '{print $1}' passwd.txt`

    do

    j=`awk -v I="$i" '{if(I==$1)print $2}' passwd.txt`

    expect /root/shell/login.exp $i $j

    done

     

    3.passwd.txt

    192.168.0.2  password2

    192.168.0.3  password3

     

     13.

     

    expect {

    "?assword:" {                    

     

     #此大括号内是逐条执行,不存在if关系

     

     

     

     

    send "$PASSWORD\r"     

    exp_continue

    }

    }

    展开全文
  • expect安装

    2020-11-20 14:13:34
    expect是一个用来处理交互的命令。借助expect,我们可以将交互过程写在一个脚本上,使之自动化完成。形象的说,ssh登录,ftp登录等都符合交互的定义。 expect是交互性很强的脚本语言,但是expect依赖于tcl,但linux...
  • expect的用法

    2017-10-31 09:57:28
    expect用法
  • centos shell编程4【分发系统】 服务器标准化 mkpasswd 生成密码的工具 expect讲解 expect传递参数 expect自动同步文件 expect指定host和要同步的文件 expect文件分发系统 expect自动发送密钥脚本 Linux脚本执行方式...
  • expect用法

    2019-04-28 15:41:19
    expect用法 1. [#!/usr/bin/expect] 这一行告诉操作系统脚本里的代码使用那一个shell来执行。这里的expect其实和linux下的bash、windows下的cmd是一类东西。 注意:这一行需要在脚本的第一行。 2. [set time...
  • maven引入log4j、slf4j冲突,java.…

    千次阅读 2017-07-13 09:45:39
    tomcat启动报错:java.lang.LinkageError: loader constraint violation: when resolving ..."org.slf4j.impl.StaticLoggerBinder.getLoggerFactory()Lorg/slf4j/ILoggerFactory;" the class loader (instance of org
  • expect 使用说明

    2019-12-13 20:57:50
    EXPECT(1) General Commands Manual EXPECT(1) NAME expect - programmed dialogue with interactive programs, Versi...
  • expect 用法

    千次阅读 2011-05-30 16:00:00
    清单1.登录 SSHD 服务器的自动化脚本 #!/usr/bin/expect # 设置超时时间为 60 秒 set timeout 60 # 设置要登录的主机 IP 地址 set host 192.168.0.4 # 设置以什么名字的用户登录
  • expect基本用法

    千次阅读 2013-08-24 21:35:52
    /usr/bin/expect]  这一行告诉操作系统脚本里的代码使用那一个shell来执行。这里的expect其实和linux下的bash、windows下的cmd是一类东西。  注意:这一行需要在脚本的第一行。  2. [set timeout 30]  ...
  • expect-5.21-setup.zip

    2021-07-15 02:22:54
    expect-5.21-setup.zip

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 19,084
精华内容 7,633
关键字:

Expect4j