web-dev-qa-db-fra.com

Comment obtenir la sortie de la commande jsch Shell dans String

J'utilise une bibliothèque JSCH -SSH pour exécuter la commande dans le canal "Shell", mais je n'arrive pas à trouver un moyen de faire 2 choses: -

1) Comment savoir si la commande est complètement exécutée sur une boîte Unix distante?

2) Comment capturer la sortie de la commande dans String, au lieu de l'imprimer sur la console System.out?

Ci-dessous mon extrait de code qui fonctionne bien pour afficher la sortie de la commande Shell sur system.out

REMARQUE: je ne veux PAS utiliser le canal "exec" car il démarre un nouveau processus pour chaque commande et ne se souvient pas des variables "session" qui ont été exportées. Je dois utiliser le canal "Shell".

Ci-dessous mon extrait de code. Toute aide est appréciée.Merci de votre temps.

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();
}
12
vishy

Pour 2) vous pouvez utiliser ByteArrayOutputStream

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

puis créez une nouvelle chaîne à partir de new String(baos.toByteArray())

Pour 1 avez-vous essayé d'utiliser 2> & 1 à la fin de votre commande?

String commandToRun = "ls /tmp/*.log 2>&1 \n";
7
Nikola Dimitrovski

Ma solution n'est peut-être plus nécessaire pour l'OP, mais toute autre personne qui recherche une solution pour couvrir les deux conditions 1) attend que les commandes se terminent sur la machine distante; et 2) capturer la sortie sous forme de chaîne; vous pouvez essayer ceci:

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();
}
}

Cette solution est également utile si vous devez envoyer plusieurs commandes à la fois et garder le canal ouvert pour le réutiliser plus tard.

18
Mihail

En prenant l'exemple fourni par Mihail, d'autres informations sur les internets et les commentaires de Martin, voici une solution retravaillée utilisant exec. Notez que l'ouverture d'une session permet d'envoyer plusieurs commandes, chacune ouvrant son propre canal d'entrée/sortie.

Rant: Je n'aime vraiment pas avoir à obtenir le processus ' [~ # ~] output [~ # ~] stream to write à. Quel paradigme ennuyeux (du moins pour moi). Ce que je voulais, c'était le flux d'entrée des processus dans lequel écrire ma sortie, et j'ai eu du mal à comprendre qu'il était inversé. Est-ce juste moi ou les éléments suivants (pseudocode) n'ont-ils pas plus de sens ??

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

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

Quoi qu'il en soit, merci à Mihail et Martin pour leurs commentaires/contributions.

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();
        }
    }
}
5
gdbj