13 Stimmen

Mehrere Befehle über die JSch-Shell

Ich habe versucht, mehrere Befehle über das SSH-Protokoll unter Verwendung der JSch-Bibliothek auszuführen. Aber ich scheine festzustecken und kann keine Lösung finden. Die setCommand() Methode kann nur einzelne Befehle pro Sitzung ausführen. Ich möchte aber die Befehle sequentiell ausführen, genau wie die connectbot-App auf der Android-Plattform. Bis jetzt ist mein Code:

package com.example.ssh;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Properties;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import com.jcraft.jsch.Channel;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;

public class ExampleSSH extends Activity {
    /** Called when the activity is first created. */
    EditText command;
    TextView result;
    Session session;
    ByteArrayOutputStream baos;
    ByteArrayInputStream bais;
    Channel channel;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        bais = new ByteArrayInputStream(new byte[1000]);
        command = (EditText) findViewById(R.id.editText1);
        result  = (TextView) findViewById(R.id.terminal);
    }

    public void onSSH(View v){
        String username = "xxxyyyzzz";
        String password = "aaabbbccc";
        String host     = "192.168.1.1"; // sample ip address
        if(command.getText().toString() != ""){
            JSch jsch = new JSch();
            try {
                session = jsch.getSession(username, host, 22);
                session.setPassword(password);

                Properties properties = new Properties();
                properties.put("StrictHostKeyChecking", "no");
                session.setConfig(properties);
                session.connect(30000);

                channel = session.openChannel("shell");
                channel.setInputStream(bais);
                channel.setOutputStream(baos);
                channel.connect();

            } catch (JSchException e) {
                // TODO Auto-generated catch block
                Toast.makeText(this, e.getMessage(), Toast.LENGTH_LONG).show();
            }
        }
        else{
            Toast.makeText(this, "Command cannot be empty !", Toast.LENGTH_LONG).show();
        }
    }

    public void onCommand(View v){
        try {
            bais.read(command.getText().toString().getBytes());
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        baos = new ByteArrayOutputStream();
        channel.setOutputStream(baos);
        result.setText(baos.toString());

    }
}

Der Code scheint mit dem Server verbunden zu werden, aber ich denke, dass es ein Problem mit den Eingabe- und Ausgabe-Array-Puffern gibt, da es überhaupt keine Ausgabe gibt. Kann mir bitte jemand erklären, wie man die Ein- und Ausgabe zum und vom Server richtig handhabt, um die gewünschte Ausgabe zu erhalten?

13voto

Paŭlo Ebermann Punkte 70779

Wenn Sie die Ein- und Ausgänge der einzelnen Befehle nicht unterscheiden müssen, kann die Antwort von Aaron (alle Befehle in einer Reihe, getrennt durch \n o ; ) ist in Ordnung.

Wenn Sie sie getrennt bearbeiten müssen oder die späteren Befehle nicht kennen, bevor die früheren beendet sind: Sie können mehrere exec -Kanäle für dieselbe Sitzung (d. h. Verbindung), eine nach der anderen (d. h. nachdem die vorherige geschlossen wurde). Jeder hat seinen eigenen Befehl. (Aber sie teilen sich keine Umgebung, so dass ein cd Befehl in der ersten hat keine Auswirkungen auf spätere).

Sie müssen lediglich dafür sorgen, dass die Sitzung Objekt zu verwenden und nicht für jeden Befehl ein neues zu erstellen.

Eine weitere Option wäre eine Schalenkanal und dann die einzelnen Befehle als Eingabe (d. h. über einen Stream) an die entfernte Shell übergeben. Allerdings muss man dann aufpassen, dass man die Eingabe eines Befehls nicht mit dem nächsten Befehl vermischt (d.h. das funktioniert nur, wenn man weiß, was die Befehle tun, oder wenn man einen interaktiven Benutzer hat, der sowohl die Eingabe des Befehls als auch des nächsten Befehls liefern kann und weiß, welcher Befehl wann verwendet werden soll).

12voto

Aaron Digulla Punkte 308693

Der Befehl ist ein String und kann alles sein, was die entfernte Shell akzeptiert. Versuchen Sie

cmd1 ; cmd2 ; cmd3

um mehrere Befehle nacheinander auszuführen. Oder

cmd1 && cmd2 && cmd3

um Befehle auszuführen, bis einer fehlschlägt.

Auch das könnte funktionieren:

cmd1
cmd2
cmd3

oder in Java:

channel.setCommand("cmd1\ncmd2\ncmd3");

Nebenbemerkung: Geben Sie Kennwörter und Benutzernamen nicht in den Code ein. Legen Sie sie in eine Eigenschaftsdatei und verwenden Sie eine Systemeigenschaft, um den Namen der Eigenschaftsdatei anzugeben. Auf diese Weise können Sie die Datei auch außerhalb des Projekts aufbewahren und sicherstellen, dass Passwörter/Benutzernamen nicht nach außen dringen.

4voto

Scott Izu Punkte 2099

Richten Sie ein SCPInfo-Objekt ein, das den Benutzernamen, das Kennwort, port:22 und ip enthält.

    List<String> commands = new ArrayList<String>();
    commands.add("touch test1.txt");
    commands.add("touch test2.txt");
    commands.add("touch test3.txt");
    runCommands(scpInfo, commands);

public static void runCommands(SCPInfo scpInfo, List<String> commands){
    try {
        JSch jsch = new JSch();
        Session session = jsch.getSession(scpInfo.getUsername(), scpInfo.getIP(), scpInfo.getPort());
        session.setPassword(scpInfo.getPassword());
        setUpHostKey(session);
        session.connect();

        Channel channel=session.openChannel("shell");//only shell  
        channel.setOutputStream(System.out); 
        PrintStream shellStream = new PrintStream(channel.getOutputStream());  // printStream for convenience 
        channel.connect(); 
        for(String command: commands) {
            shellStream.println(command); 
            shellStream.flush();
        }

        Thread.sleep(5000);

        channel.disconnect();
        session.disconnect();
    } catch (Exception e) { 
        System.err.println("ERROR: Connecting via shell to "+scpInfo.getIP());
        e.printStackTrace();
    }
}

private static void setUpHostKey(Session session) {
    // Note: There are two options to connect
    // 1: Set StrictHostKeyChecking to no
    //    Create a Properties Object
    //    Set StrictHostKeyChecking to no
    //    session.setConfig(config);
    // 2: Use the KnownHosts File
    //    Manually ssh into the appropriate machines via unix
    //    Go into the .ssh\known_hosts file and grab the entries for the hosts
    //    Add the entries to a known_hosts file
    //    jsch.setKnownHosts(khfile);
    java.util.Properties config = new java.util.Properties();
    config.put("StrictHostKeyChecking", "no");
    session.setConfig(config);
}

3voto

Martin Prikryl Punkte 164336

Vermeiden Sie die Verwendung des "Shell"-Kanals so weit wie möglich. Der "Shell"-Kanal ist für die Durchführung einer interaktiven Sitzung gedacht, nicht für die automatische Ausführung eines Befehls. Der "Shell"-Kanal hat viele unerwünschte Nebeneffekte.

Um die Ausführung eines Befehls zu automatisieren, verwenden Sie den Kanal "exec".


Normalerweise können Sie so viele "exec"-Kanäle öffnen, wie Sie benötigen, und jeden davon zur Ausführung eines Ihrer Befehle verwenden. Sie können die Kanäle nacheinander oder sogar parallel öffnen.

Ein vollständiges Beispiel für die Verwendung des "exec"-Kanals finden Sie unter JSch Exec.java Beispiel .

Auf diese Weise wird jeder Befehl in einer isolierten Umgebung ausgeführt. Das mag ein Vorteil sein, kann aber in manchen Fällen auch unerwünscht sein.


Wenn Sie Befehle so ausführen müssen, dass sich frühere Befehle auf spätere Befehle auswirken (wie das Ändern eines Arbeitsverzeichnisses oder das Setzen einer Umgebungsvariablen), müssen Sie alle Befehle im selben Kanal ausführen. Verwenden Sie dazu ein geeignetes Konstrukt der Shell des Servers. Auf den meisten Systemen können Sie Semikolons verwenden:

execChannel.setCommand("command1 ; command2 ; command3");

Auf *nix-Servern können Sie auch && damit die folgenden Befehle nur ausgeführt werden, wenn die vorherigen Befehle erfolgreich waren:

execChannel.setCommand("command1 && command2 && command3");

Siehe auch Ausführen einer Liste von Befehlen aus einer ArrayList mit JSch exec in Java .

In vielen Fällen müssen Sie nicht einmal mehrere Befehle verwenden.

Zum Beispiel anstelle dieser Sequenz, die Sie bei der interaktiven Verwendung der Shell machen könnten:

cd /path
ls

Das können Sie tun:

ls /path

Am kompliziertesten ist die Situation, wenn die Befehle voneinander abhängen und Sie die Ergebnisse der vorherigen Befehle verarbeiten müssen, bevor Sie mit anderen Befehlen fortfahren.

Wenn Sie ein solches Bedürfnis haben, deutet dies in der Regel auf ein schlechtes Design hin. Überlegen Sie gut, ob dies wirklich die einzige Lösung für Ihr Problem ist. Oder ziehen Sie die Implementierung eines serverseitigen Shell-Skripts in Betracht, um die Logik zu implementieren, anstatt dies aus der Ferne über Java-Code zu tun. Shell-Skripte haben viel leistungsfähigere Techniken, um die Ergebnisse früherer Befehle zu überprüfen, als die SSH-Schnittstelle in JSch.

Wie auch immer, siehe Der JSch-Shell-Kanal führt die Befehle nacheinander aus und prüft das Ergebnis, bevor er fortfährt. .


Nebenbemerkung: Verwenden Sie nicht StrictHostKeyChecking=no . Siehe JSch SFTP-Sicherheit mit session.setConfig("StrictHostKeyChecking", "no"); .

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X