web-dev-qa-db-fra.com

Quelle est la différence entre le canal 'Shell' et le canal 'exec' dans JSch

Je veux pouvoir envoyer plusieurs commandes consécutives représentées sous forme de chaînes dans une application Java) à un serveur SSH pour exécution. Dois-je utiliser:

Channel channel = session.openChannel("Shell");

-ou-

Channel channel = session.openChannel("exec");
28
Martin Klosi

Avec le canal Shell, le Shell (sous unix c'est sh ou bash ou quelque chose comme ça, sous Windows c'est généralement cmd.exe) est démarré et la console est créée (la même que vous voyez à l'écran si vous les exécutez localement). Vous disposez d'une invite que vous pouvez analyser ou utiliser pour détecter la fin de la commande.

Avec le canal de commande, une instance Shell est démarrée pour chaque commande (en fait, le canal est ouvert pour chaque commande) et une commande est passée en tant que paramètre pour le shell (sous Windows, cela ressemblerait à "cmd.exe/c").

Il est plus facile d'utiliser le canal de commande car vous n'avez pas besoin de traiter l'invite de commande.

Vous trouverez un aperçu des différences et des similitudes entre ces flux sur " Shell, Exec ou Subsystem Channel " dans le wiki JSch. Voici quelques détails pour votre cas d'utilisation.

Dans le canal exec , les commandes proviennent de la chaîne de commande que vous avez donnée avec setCommand(). Le serveur SSH les transmettra immédiatement au Shell (en utilisant quelque chose comme bash -c '<command>').

Ils seront tous exécutés si le Shell ne se ferme pas avant pour une raison quelconque. (Vous pouvez envoyer ici un script Shell complet qui implémente une certaine logique en utilisant if et similaire, si cela est souhaité.)

Ainsi, pour exécuter plusieurs commandes, vous pouvez les passer au canal exec en les séparant par ; ou nouvelle ligne (\n). Comme vous ne pouvez pas attendre les résultats avant de donner toutes les commandes, vous ne pouvez utiliser ici que plusieurs canaux d'exécution (mais comme chaque canal génère un nouveau Shell, ils ne conservent pas d'état entre eux, comme le répertoire de travail ou les variables Shell).

Dans le canal Shell , le shell lit les entrées du flux et interprète la première ligne comme une commande (ou plusieurs).

Ensuite, il exécutera cette commande. La commande elle-même peut lire plus d'entrée du flux, si elle le souhaite.

Ensuite, le shell lit la ligne suivante, l'interprète comme une commande et s'exécute.

(Dans certains cas, le shell doit lire plusieurs lignes, par exemple pour les chaînes longues ou les commandes composées comme les boucles if ou.)

Cela continuera jusqu'à la fin du flux (par exemple stream.close () à vos côtés) ou en exécutant une commande de sortie explicite.

Si vous n'envoyez aucune entrée au Shell via le flux d'entrée/sortie des canaux, le Shell attendra simplement jusqu'à ce que vous en envoyiez plus ou fermiez le flux. Ainsi, vous pouvez lire tranquillement la sortie d'une commande, faire quelques calculs côté client, puis décider quelle commande envoyer ensuite.

Assurez-vous simplement de ne pas mélanger l'entrée à une commande avec le texte de la commande suivante - de préférence, n'utilisez aucune commande qui lira à partir de l'entrée standard.

15
Paŭlo Ebermann

eh bien, j'ai découvert que cela fonctionne et c'est vraiment pratique si vous voulez préserver l'état comme le ferait un Shell normal:

Session session = jsch.getSession(user, Host, 22);

Channel channel = session.openChannel("Shell");

OutputStream inputstream_for_the_channel = channel.getOutputStream();
PrintStream commander = new PrintStream(inputstream_for_the_channel, true);

channel.setOutputStream(System.out, true);

channel.connect();

commander.println("ls -la");    
commander.println("cd folder");
commander.println("ls -la");
commander.println("exit");
commander.close();

do {
    Thread.sleep(1000);
} while(!channel.isEOF());

session.disconnect();

Tu pourrais changer

channel.setOutputStream(System.out, true);

avec

InputStream outputstream_from_the_channel = channel.getInputStream();
BufferedReader br = new BufferedReader(new InputStreamReader(outputstream_from_the_channel));
String line;
while ((line = br.readLine()) != null)
    System.out.print(line+"\n");

si vous voulez plus de contrôle sur la sortie.

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

ÉDITÉ: suivi

pourquoi parfois les commandes que j'envoie via PrintStream apparaissent de manière aléatoire dans la sortie. c'est-à-dire le code suivant:

Shell[0].println("cd ..");
Shell[0].println("cd ..");
Shell[0].println("ls -la");
Shell[0].println("exit");

produit ceci: (marqués avec {chose} sont des choses qui ne devraient pas être là!)

Dernière connexion: Jeu 21 juil 21:49:13 2011 depuis la passerelle

Manifestes: trunk-latest

[Hôte ~] $ cd ..
{cd ..} [Accueil hôte] $
[Accueil hôte] $ cd ..
[Hôte /] $
[Hôte /] $ ls -la
{sortie}

9999 au total
---------- 27 racine racine 4096 26 janvier 2010.
---------- 27 racine racine 4096 26 janvier 2010 ..
---------- 1 racine root 0 mars 14 19:16 .autojyk
---------- 1 racine root 0 9 février 2009 .automan
---------- 1 racine root 3550 14 mai 2010 .bash_history
d --------- 2 racine racine 4096 26 avril 04:02 mis
d --------- 5 root root 4024 25 avril 19:31 boot
[m [Hôte /] $
[Hôte /] $ exit
Se déconnecter

6
Martin Klosi

Ce n'est pas tellement à propos de JSch. Il s'agit de la façon dont le serveur implémente les deux canaux.


En commun * nix serveur OpenSSH:

  • Le canal Shell exécute un shell de connexion (comme si vous vous connectez avec le client de terminal SSH). Le shell présentera ensuite une invite de commande et attendra que le client/utilisateur tape les commandes. L'objectif du canal Shell est d'implémenter une session Shell interactive. C'est quelque chose que l'on fera très très rarement. Si vous le faites, vous souhaitez généralement utiliser l'émulation de terminal.

    Le canal Shell est évidemment utilisé par les clients du terminal SSH (comme OpenSSH ssh ou PuTTY), dans des circonstances normales.

    Le canal Shell est une boîte noire avec une entrée et une sortie. L'entrée et la sortie n'ont pas de structure. Si vous exécutez par exemple une commande en l'envoyant à l'entrée, vous ne pourrez jamais dire quand elle s'est terminée. Si vous envoyez deux commandes à la file d'attente d'entrée, vous ne pourrez pas distinguer quelle sortie provient de quelle commande.

  • La commande exec prend une commande comme "argument" et l'exécute dans un environnement isolé - toujours via le shell par défaut de l'utilisateur, mais pas comme un shell "de connexion", ce qui peut entraîner des différences importantes dans l'exécution de la commande.

    Le but du canal exec est d'automatiser l'exécution d'une commande. Donc, généralement, vous ne voulez pas utiliser d'émulation de terminal, pour éviter la commande de faire des choses fantaisistes comme la pagination, la coloration et principalement les confirmations interactives.

    exec channel est utilisé par OpenSSH ssh ou PuTTY plink, lorsque vous spécifiez une commande à exécuter sur sa ligne de commande:

    ssh user@Host command
    

Avec des serveurs SSH moins courants, la différence peut être encore plus importante. Certains serveurs peuvent même ne pas prendre en charge l'un des canaux. Il est également assez courant qu'ils semblent prendre en charge les deux, mais l'un d'eux (généralement le exec) est complètement cassé.


Il y a une question similaire pour Python/Paramiko:
Quelle est la différence entre exec_command et envoyer avec invoke_Shell () sur Paramiko?

0
Martin Prikryl