通过 Java JSch 库在远程机器上执行本地脚本

Execute a local script on a remote machine through Java JSch library

我正在监控一组远程服务器上的错误日志。为此,我正在监视在一段时间内修改的日志。

我正在使用 Jsch Java library 创建一个 SSH connection 并执行命令。正如我所搜索的,多个命令可以通过 ;

分隔不同的命令来远程机器

为此,我有一个文本文件,其中包含我正在建立连接的远程主机的列表。我意识到我可以获得通过以下命令修改的所有文件的列表

find . -mmin -60 -type f -exec ls {} +

不,我可以遍历文件和 grep for the Error。为此,我编写了以下脚本

logTestScript.sh

#!/bin/bash

cd /log
searchString='<Error ErrorCode="java.sql.SQLException"'
files=`find . -mmin -60 -type f -exec ls {} +`
pwd
hostname
echo ${files}
for file in ${files[@]}
do
        if grep -Fq "$searchString" $file
        then
                echo "String found in $file"
        fi
done

现在这个脚本存在于我运行正在使用java程序的当前包中,我希望运行每个远程主机上的这个脚本。

下面是我当前使用 ChannelExec

的代码
private void getSSHConnection() throws IOException, JSchException {

        username = props.getProperty("host_username");
        password = props.getProperty("host_password");
        log_traceback_interval = props.getProperty("fetch_logs_interval_minutes");
        errorString = props.getProperty("error_message_log");

        System.out.println(username + " " + password + " " + errorString);

        // Get the hostnames resource file
        String hostnameResourcePath = envObj.getAgentHostnamesConfig(TargetEnvironment, ManagementSystem);
        InputStream hostInpStream = this.getClass().getResourceAsStream(hostnameResourcePath);
        BufferedReader hostnameReader = new BufferedReader(new InputStreamReader(hostInpStream));

        String host = null;

        while((host = hostnameReader.readLine()) != null) {

            System.out.println(host);

            // get a JSch connection object
            JSch jschObj = new JSch();
            Session session = jschObj.getSession(username, host);

            // Disable StrictHost key checking
            Properties config = new Properties();
            config.put("StrictHostKeyChecking", "no");
            session.setConfig(config);

            session.setPassword(password);
            session.connect();

            //Execute channel instance
            ChannelExec channelExec = (ChannelExec)session.openChannel("exec");
            InputStream in = channelExec.getInputStream();

            //Bash script command with arguments
            StringBuilder sb = new StringBuilder();
            String command = sb.append("cd /log").append(";")
                                .append("echo `find . -mmin -60 -type f -exec ls {} +`").append(";")
                                .toString();   

            System.out.println(command);

            //Run the script on host
            channelExec.setCommand(command);    //I want to run the script here instead of each command as it uses for loop

            channelExec.setInputStream(null);

            channelExec.setErrStream(System.err);

            InputStream monitorLogs = channelExec.getInputStream();

            channelExec.connect();

            //Read the logs
            BufferedReader logReader = new BufferedReader(new InputStreamReader(monitorLogs));
            String logLine;

            while((logLine = logReader.readLine()) != null) {

                System.out.println(logLine);
            }

            //Close the connections
            channelExec.disconnect();
            session.disconnect();
        }




    }

我能想到的一个解决方案是将脚本通过 SCP 发送到远程主机,然后在那里执行。

但是有没有一些简单的解决方案,即使不编写脚本并且只在远程服务器上执行命令或执行脚本,我也可以获得相同的结果。

感谢帮助

您不能在服务器上执行本地脚本。

你有两个选择(你显然知道):

  • 上传脚本并执行(如果需要,之后删除)。
  • 执行脚本的内容。如您所知,只需将所有行与 ; 连接起来。

    不过要小心 forifdothen 之后不应该有 ;

    cd /log ; searchString='<Error ErrorCode="java.sql.SQLException"' ; files=`find . -mmin -60 -type f -exec ls {} +` ; echo `pwd` ; echo `hostname` ; echo ${files} ; for file in ${files[@]} ; do if grep -Fq "$searchString" $file ; then echo "String found in $file" ; fi ; done
    
  • 与前面的方法类似,您可以执行 bash -s 并将脚本的内容(命令)写入其输入。

    ChannelExec channelExec = (ChannelExec)session.openChannel("exec");
    channelExec.setCommand("bash -s");
    channelExec.connect();
    OutputStream out = channel.getOutputStream();
    FileInputStream fis = new FileInputStream("testScript.sh");
    byte[] buf = new byte[1024];
    while (true)
    {
        int len = fis.read(buf, 0, buf.length);
        if (len <= 0) break;
        out.write(buf, 0, len);
    }
    

    以上基本上是从看似无关的SCP upload example复制而来的-该示例实际上将本地文件提供给远程scp进程-所以它实际上非常相关。


Ntb,"echo `pwd`" 和 "echo `hostname`" 没有意义。直接使用"pwd"和"hostname"。

为了在远程服务器上执行脚本 script.sh,您可以使用以下代码 -

#!/usr/bin/expect
export PASSD="User@123"
expect -c 'spawn sftp user@xxxxxx.com; 
expect "*Password: ";
send "$env(PASSD)\r";
expect "ssh>";
send "cd /home/user \r";
expect "ssh>";
send "sh -x script.sh \r";
expect "ssh>";
send "exit \r"'

您可以将以上代码放在脚本中,然后在您的 if 循环的成功条件之后添加。