68 votes

Exécuter une commande via SSH avec JSch

J'essaie d'exécuter une commande par SSH avec JSch, mais JSch n'a pratiquement aucune documentation et les exemples que j'ai trouvés sont terribles. Par exemple, celui-ci ne montre pas de code pour gérer le flux de sortie. Et, celui-ci utilise une astuce peu élégante pour savoir quand arrêter la lecture du flux de sortie.

0 votes

Voir aussi Comment lire la sortie de la commande JSch ? - Qui montre comment correctement lire simultanément la sortie standard et la sortie d'erreur, afin de permettre à la commande de se terminer et de collecter toutes les sorties, y compris les erreurs.

107voto

CharityAbbott Points 930

L'exemple de code suivant, écrit en Java, vous permettra d'exécuter n'importe quelle commande sur un ordinateur étranger via SSH à partir d'un programme Java. Vous devrez inclure le fichier jar com.jcraft.jsch.

  /* 
  * SSHManager
  * 
  * @author cabbott
  * @version 1.0
  */
  package cabbott.net;

  import com.jcraft.jsch.*;
  import java.io.IOException;
  import java.io.InputStream;
  import java.util.logging.Level;
  import java.util.logging.Logger;

  public class SSHManager
  {
  private static final Logger LOGGER = 
      Logger.getLogger(SSHManager.class.getName());
  private JSch jschSSHChannel;
  private String strUserName;
  private String strConnectionIP;
  private int intConnectionPort;
  private String strPassword;
  private Session sesConnection;
  private int intTimeOut;

  private void doCommonConstructorActions(String userName, 
       String password, String connectionIP, String knownHostsFileName)
  {
     jschSSHChannel = new JSch();

     try
     {
        jschSSHChannel.setKnownHosts(knownHostsFileName);
     }
     catch(JSchException jschX)
     {
        logError(jschX.getMessage());
     }

     strUserName = userName;
     strPassword = password;
     strConnectionIP = connectionIP;
  }

  public SSHManager(String userName, String password, 
     String connectionIP, String knownHostsFileName)
  {
     doCommonConstructorActions(userName, password, 
                connectionIP, knownHostsFileName);
     intConnectionPort = 22;
     intTimeOut = 60000;
  }

  public SSHManager(String userName, String password, String connectionIP, 
     String knownHostsFileName, int connectionPort)
  {
     doCommonConstructorActions(userName, password, connectionIP, 
        knownHostsFileName);
     intConnectionPort = connectionPort;
     intTimeOut = 60000;
  }

  public SSHManager(String userName, String password, String connectionIP, 
      String knownHostsFileName, int connectionPort, int timeOutMilliseconds)
  {
     doCommonConstructorActions(userName, password, connectionIP, 
         knownHostsFileName);
     intConnectionPort = connectionPort;
     intTimeOut = timeOutMilliseconds;
  }

  public String connect()
  {
     String errorMessage = null;

     try
     {
        sesConnection = jschSSHChannel.getSession(strUserName, 
            strConnectionIP, intConnectionPort);
        sesConnection.setPassword(strPassword);
        // UNCOMMENT THIS FOR TESTING PURPOSES, BUT DO NOT USE IN PRODUCTION
        // sesConnection.setConfig("StrictHostKeyChecking", "no");
        sesConnection.connect(intTimeOut);
     }
     catch(JSchException jschX)
     {
        errorMessage = jschX.getMessage();
     }

     return errorMessage;
  }

  private String logError(String errorMessage)
  {
     if(errorMessage != null)
     {
        LOGGER.log(Level.SEVERE, "{0}:{1} - {2}", 
            new Object[]{strConnectionIP, intConnectionPort, errorMessage});
     }

     return errorMessage;
  }

  private String logWarning(String warnMessage)
  {
     if(warnMessage != null)
     {
        LOGGER.log(Level.WARNING, "{0}:{1} - {2}", 
           new Object[]{strConnectionIP, intConnectionPort, warnMessage});
     }

     return warnMessage;
  }

  public String sendCommand(String command)
  {
     StringBuilder outputBuffer = new StringBuilder();

     try
     {
        Channel channel = sesConnection.openChannel("exec");
        ((ChannelExec)channel).setCommand(command);
        InputStream commandOutput = channel.getInputStream();
        channel.connect();
        int readByte = commandOutput.read();

        while(readByte != 0xffffffff)
        {
           outputBuffer.append((char)readByte);
           readByte = commandOutput.read();
        }

        channel.disconnect();
     }
     catch(IOException ioX)
     {
        logWarning(ioX.getMessage());
        return null;
     }
     catch(JSchException jschX)
     {
        logWarning(jschX.getMessage());
        return null;
     }

     return outputBuffer.toString();
  }

  public void close()
  {
     sesConnection.disconnect();
  }

  }

Pour les tests.

  /**
     * Test of sendCommand method, of class SSHManager.
     */
  @Test
  public void testSendCommand()
  {
     System.out.println("sendCommand");

     /**
      * YOU MUST CHANGE THE FOLLOWING
      * FILE_NAME: A FILE IN THE DIRECTORY
      * USER: LOGIN USER NAME
      * PASSWORD: PASSWORD FOR THAT USER
      * HOST: IP ADDRESS OF THE SSH SERVER
     **/
     String command = "ls FILE_NAME";
     String userName = "USER";
     String password = "PASSWORD";
     String connectionIP = "HOST";
     SSHManager instance = new SSHManager(userName, password, connectionIP, "");
     String errorMessage = instance.connect();

     if(errorMessage != null)
     {
        System.out.println(errorMessage);
        fail();
     }

     String expResult = "FILE_NAME\n";
     // call sendCommand for each command and the output 
     //(without prompts) is returned
     String result = instance.sendCommand(command);
     // close only after all commands are sent
     instance.close();
     assertEquals(expResult, result);
  }

0 votes

InputStream commandOutput ne semble pas être fermé explicitement. Cela va-t-il créer une fuite ?

0 votes

@stanleyxu2005 docs.oracle.com/javase/7/docs/api/java/io/ La méthode de fermeture de l'InputStream ne fait rien.

0 votes

Le channel.getInputStream() doit être avant le channel.connect() selon la doc Java : epaul.github.io/jsch-documentation/javadoc/com/jcraft/jsch/ Je ne sais pas pourquoi...

36voto

Paŭlo Ebermann Points 35526

C'est une publicité éhontée, mais je suis juste en train de écrire une certaine ampleur Javadoc pour JSch .

En outre, il existe désormais un Manuel dans le Wiki JSch (écrit principalement par moi).


En ce qui concerne la question initiale, il n'y a pas vraiment d'exemple pour la gestion des flux. La lecture/écriture d'un flux se fait comme toujours.

Mais il n'existe tout simplement pas de moyen sûr de savoir quand une commande dans un shell est terminée en lisant simplement la sortie du shell (ceci est indépendant du protocole SSH).

Si l'interpréteur de commandes est interactif, c'est-à-dire s'il a un terminal attaché, il imprimera généralement une invite, que vous pouvez essayer de reconnaître. Mais, au moins en théorie, cette chaîne d'invite peut également apparaître dans la sortie normale d'une commande. Si vous voulez en être sûr, ouvrez un exec pour chaque commande au lieu d'utiliser un canal shell. Le canal shell est principalement utilisé pour une utilisation interactive par un utilisateur humain, je pense.

0 votes

J'en avais bien besoin, merci. JSCH est une excellente idée, mais elle semble être une bibliothèque abandonnée, sans aucune documentation. Je suis parfois déconcerté par le fait que des personnes l'utilisent dans des projets critiques.

8voto

Jay Shepherd Points 66

J'ai lutté pendant une demi-journée pour que JSCH fonctionne sans utiliser System.in comme flux d'entrée, mais en vain. J'ai essayé Ganymed http://www.ganymed.ethz.ch/ssh2/ et l'a fait fonctionner en 5 minutes. Tous les exemples semblent être destinés à une seule utilisation de l'application et aucun des exemples n'a montré ce dont j'avais besoin. L'exemple de Ganymed Basic.java Baaaboof a tout ce dont j'ai besoin.

6voto

shikhar Points 962

Utiliser ssh à partir de java ne devrait pas être aussi difficile que ce que jsch en dit. vous pourriez être mieux avec sshj .

2voto

omerkudat Points 2536

El sablonneux Le terminal a été écrit pour utiliser Jsch, mais avec une meilleure manipulation et une émulation vt102. Vous pouvez jeter un coup d'oeil au code ici. Nous l'utilisons et il fonctionne très bien.

0 votes

Cependant, il est plus facile d'y accéder à partir de GitHub (par exemple, - - - -). github.com/xliangwu/gritty ).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X