Несколько команд через оболочку JSch - PullRequest
12 голосов
/ 29 апреля 2011

Я пытался выполнить несколько команд по протоколу SSH, используя библиотеку JSch. Но я, кажется, застрял и не могу найти никакого решения. Метод setCommand() может выполнять только отдельные команды за сеанс. Но я хочу выполнять команды последовательно, как приложение connectbot на платформе Android. Пока мой код:

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

    }
}

Кажется, что код подключается к серверу, но я думаю, что есть некоторые проблемы с буферами массива ввода и вывода, потому что вообще нет вывода. Может кто-нибудь подсказать мне, как правильно обрабатывать ввод и вывод на сервер и с него, чтобы получить желаемый результат?

Ответы [ 4 ]

12 голосов
/ 29 апреля 2011

Команда является строкой и может быть любой, что принимает удаленная оболочка. Попробуйте

cmd1 ; cmd2 ; cmd3

для запуска нескольких команд в последовательности. Или

cmd1 && cmd2 && cmd3

запускать команды до тех пор, пока не произойдет сбой.

Даже это может сработать:

cmd1
cmd2
cmd3

или на Java:

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

Sidenote: Не вводите пароли и имена пользователей в код. Поместите их в файл свойств и используйте системное свойство, чтобы указать имя файла свойств. Таким образом, вы можете сохранить файл даже за пределами проекта и убедиться, что пароли / имена пользователей не просочились.

11 голосов
/ 30 апреля 2011

Если вам не нужно различать входы или выходы отдельных команд, ответ от Аарона (с указанием всех команд в строке, разделенных \n или ;) - это хорошо.

Если вам нужно обрабатывать их отдельно или вы не знаете более поздние команды до завершения предыдущих: вы можете открыть несколько exec -каналов в одном сеансе (т. Е. Соединении), один раз последругой (т.е. после того, как предыдущий был закрыт).У каждого своя команда.(Но они не разделяют окружение, поэтому команда cd в первой не влияет на последующие.)

Вам просто нужно позаботиться о том, чтобы объект Session вокруг, а не создавать новую для каждой команды.

Другим вариантом будет канал оболочки , а затем передача отдельных команд в удаленную оболочку в качестве ввода (т. е. через поток),Но тогда вы должны позаботиться о том, чтобы не смешивать ввод для одной команды со следующей командой (т.е. это работает, только если вы знаете, что делают команды, или если у вас есть интерактивный пользователь, который может предоставить оба ввода для команды иследующая команда и знает, какая из них будет использоваться когда.)

3 голосов
/ 26 марта 2012

Настройка объекта SCPInfo для хранения имени пользователя, пароля, порта: 22 и IP-адреса.

    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);
}
0 голосов
/ 19 сентября 2018

Старайтесь не использовать канал "shell" в максимально возможной степени.Канал «оболочка» предназначен для реализации интерактивного сеанса, а не для автоматизации выполнения команды.С каналом "shell" вы столкнетесь со многими нежелательными побочными эффектами.

Чтобы автоматизировать выполнение команды, используйте канал "exec".


Обычно вы можете открыть столько "exec""каналы, как вам нужно, используя каждый для выполнения одной из ваших команд.Вы можете открывать каналы последовательно или даже параллельно.

Полный пример использования канала "exec" см. JSch Exec.java пример .

Таким образомкаждая команда выполняется в изолированной среде.Это может быть преимуществом, но в некоторых случаях это также может быть нежелательно.


Если вам нужно выполнять команды так, чтобы предыдущие команды влияли на более поздние команды (например, изменение рабочего каталога или настройка средыпеременная), вы должны выполнить все команды в одном канале.Для этого используйте соответствующую конструкцию серверной оболочки.В большинстве систем вы можете использовать точки с запятой:

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

На * nix-серверах вы также можете использовать &&, чтобы следующие команды выполнялись только при успешном выполнении предыдущих команд:

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

См. Также Выполнение списка команд из ArrayList с использованием JSch exec в Java .


Наиболее сложная ситуация возникает, когда команды зависят друг от друга, и вам нужно обрабатыватьрезультаты предыдущих команд, прежде чем переходить к другим командам.

Когда у вас есть такая необходимость, это обычно указывает на плохой дизайн.Подумайте, если это действительно единственное решение вашей проблемы.Или рассмотрите возможность реализации сценария оболочки на стороне сервера для реализации логики, вместо того чтобы делать это удаленно из кода Java.Сценарии оболочки имеют гораздо более мощные методы для проверки результатов предыдущих команд, чем при использовании SSH-интерфейса в JSch.

В любом случае, смотрите Канал оболочки JSch выполняет команды одну за другой, перед тем как продолжить .


Примечание: не используйте StrictHostKeyChecking=no.См. Безопасность JSch SFTP с помощью session.setConfig («StrictHostKeyChecking», «no»); .

...