Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to get jsch shell command output in String

Tags:

java

ssh

jsch

I am using a JSCH -SSH library to execute command in "shell" channel, but unable to find a way to do 2 things:-

1) How to find whether the command is completely executed on remote unix box ?

2) How to capture the command output in String , instead of printing it on System.out console ?

Below is my code snippet which works fine to display shell command output on system.out

NOTE :I do NOT want to use ,"exec" channel, as it starts a new process for each command and does not remember "session" variables which were exported. I must use "shell" channel.

Below is my code snippet. Any help is appreciated.Thanks for your time.

try{

  String commandToRun = "ls /tmp/*.log \n";
  if(channel.isClosed())
      channel=session.openChannel("shell");
  byte[] bytes = commandToRun.getBytes();
  ByteArrayInputStream bais=new ByteArrayInputStream(bytes);
  channel.setInputStream(bais);
  InputStream ins=channel.getInputStream();
  channel.connect();
  channel.setOutputStream(System.out);//This prints on console. Need 2 capture in String somehow?

  //in-efficient way to allow command to execute completely on remote Unix machine
  //DO NOT know a better way, to know when command is executed completely
  Thread.sleep(5000L);
}
 catch(Exception e){
  System.out.println("Exception  in executeCommand() --->"+ e.getMessage());
  e.printStackTrace();
}
like image 662
vishy Avatar asked Sep 11 '14 13:09

vishy


3 Answers

My solution may not be needed anymore for the OP, but anyone else who is searching for a solution to cover both conditions 1) waiting for the commands to finish on remote machine; and 2) capturing output as string; you can try this:

public class SshConnectionManager {

private static Session session;
private static ChannelShell channel;
private static String username = "";
private static String password = "";
private static String hostname = "";


private static Session getSession(){
    if(session == null || !session.isConnected()){
        session = connect(hostname,username,password);
    }
    return session;
}

private static Channel getChannel(){
    if(channel == null || !channel.isConnected()){
        try{
            channel = (ChannelShell)getSession().openChannel("shell");
            channel.connect();

        }catch(Exception e){
            System.out.println("Error while opening channel: "+ e);
        }
    }
    return channel;
}

private static Session connect(String hostname, String username, String password){

    JSch jSch = new JSch();

    try {

        session = jSch.getSession(username, hostname, 22);
        Properties config = new Properties(); 
        config.put("StrictHostKeyChecking", "no");
        session.setConfig(config);
        session.setPassword(password);

        System.out.println("Connecting SSH to " + hostname + " - Please wait for few seconds... ");
        session.connect();
        System.out.println("Connected!");
    }catch(Exception e){
        System.out.println("An error occurred while connecting to "+hostname+": "+e);
    }

    return session;

}

private static void executeCommands(List<String> commands){

    try{
        Channel channel=getChannel();

        System.out.println("Sending commands...");
        sendCommands(channel, commands);

        readChannelOutput(channel);
        System.out.println("Finished sending commands!");

    }catch(Exception e){
        System.out.println("An error ocurred during executeCommands: "+e);
    }
}

private static void sendCommands(Channel channel, List<String> commands){

    try{
        PrintStream out = new PrintStream(channel.getOutputStream());

        out.println("#!/bin/bash");
        for(String command : commands){
            out.println(command);
        }
        out.println("exit");

        out.flush();
    }catch(Exception e){
        System.out.println("Error while sending commands: "+ e);
    }

}

private static void readChannelOutput(Channel channel){

    byte[] buffer = new byte[1024];

    try{
        InputStream in = channel.getInputStream();
        String line = "";
        while (true){
            while (in.available() > 0) {
                int i = in.read(buffer, 0, 1024);
                if (i < 0) {
                    break;
                }
                line = new String(buffer, 0, i);
                System.out.println(line);
            }

            if(line.contains("logout")){
                break;
            }

            if (channel.isClosed()){
                break;
            }
            try {
                Thread.sleep(1000);
            } catch (Exception ee){}
        }
    }catch(Exception e){
        System.out.println("Error while reading channel output: "+ e);
    }

}

public static void close(){
    channel.disconnect();
    session.disconnect();
    System.out.println("Disconnected channel and session");
}


public static void main(String[] args){
    List<String> commands = new ArrayList<String>();
    commands.add("ls -l");

    executeCommands(commands);
    close();
}
}

This solution is also useful if you need to send multiple commands at a time and keep the channel open to reuse it later.

like image 119
Mihail Avatar answered Nov 03 '22 01:11

Mihail


For 2) u can use ByteArrayOutputStream

final ByteArrayOutputStream baos = new ByteArrayOutputStream();
channel.setOutputStream(baos);

and then create new string from new String(baos.toByteArray())

For 1 have you tried to use 2>&1 at the end of your command?

String commandToRun = "ls /tmp/*.log 2>&1 \n";
like image 22
Nikola Dimitrovski Avatar answered Nov 03 '22 01:11

Nikola Dimitrovski


Taking the example provided by Mihail, other info on the internets, and the feedback from Martin, here's a reworked solution using exec. Note that opening a session allows multiple commands to be sent, each one opening it's own channel for input/output.

Rant:I really dislike having to get the process' OUTPUT stream to write to. What an annoying paradigm (at least for me). What I wanted is the processes input stream to write my output to, and had an amazingly difficult time working out that it's inverted. Is it just me or does the following (pseudocode) not make way more sense??

channel.getInputStream().write("here's some text to write into my channel.");

String ret = channel.getOutputStream().getOutput();

Anyways, thanks to Mihail and Martin for their comments / input.

public class SSHConnectionManager {

    private Session session;

    private String username = "user";
    private String password = "password";
    private String hostname = "myhost";

    public SSHConnectionManager() { }

    public SSHConnectionManager(String hostname, String username, String password) {
        this.hostname = hostname;
        this.username = username;
        this.password = password;
    }

    public void open() throws JSchException {
        open(this.hostname, this.username, this.password);
    }

    public void open(String hostname, String username, String password) throws JSchException{

        JSch jSch = new JSch();

        session = jSch.getSession(username, hostname, 22);
        Properties config = new Properties(); 
        config.put("StrictHostKeyChecking", "no");  // not recommended
        session.setConfig(config);
        session.setPassword(password);

        System.out.println("Connecting SSH to " + hostname + " - Please wait for few seconds... ");
        session.connect();
        System.out.println("Connected!");
    }

    public String runCommand(String command) throws JSchException, IOException {

        String ret = "";

        if (!session.isConnected())
            throw new RuntimeException("Not connected to an open session.  Call open() first!");

        ChannelExec channel = null;
        channel = (ChannelExec) session.openChannel("exec");

        channel.setCommand(command);
        channel.setInputStream(null);

        PrintStream out = new PrintStream(channel.getOutputStream());
        InputStream in = channel.getInputStream(); // channel.getInputStream();

        channel.connect();

        // you can also send input to your running process like so:
        // String someInputToProcess = "something";
        // out.println(someInputToProcess);
        // out.flush();

        ret = getChannelOutput(channel, in);

        channel.disconnect();

        System.out.println("Finished sending commands!");

        return ret;
    }


    private String getChannelOutput(Channel channel, InputStream in) throws IOException{

        byte[] buffer = new byte[1024];
        StringBuilder strBuilder = new StringBuilder();

        String line = "";
        while (true){
            while (in.available() > 0) {
                int i = in.read(buffer, 0, 1024);
                if (i < 0) {
                    break;
                }
                strBuilder.append(new String(buffer, 0, i));
                System.out.println(line);
            }

            if(line.contains("logout")){
                break;
            }

            if (channel.isClosed()){
                break;
            }
            try {
                Thread.sleep(1000);
            } catch (Exception ee){}
        }

        return strBuilder.toString();   
    }

    public void close(){        
        session.disconnect();
        System.out.println("Disconnected channel and session");
    }


    public static void main(String[] args){

        SSHConnectionManager ssh = new SSHConnectionManager();
        try {
            ssh.open();
            String ret = ssh.runCommand("ls -l");

            System.out.println(ret);
            ssh.close();

        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
like image 45
gdbj Avatar answered Nov 03 '22 00:11

gdbj