Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multiple commands through JSch shell

I was trying to execute multiple commands through SSH protocol using the JSch library. But I seem to have stuck and cannot find any solution. The setCommand() method can only execute single commands per session. But I want to execute the commands sequentially just like the connectbot app on the Android platform. So far my code is:

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

    }
}

The code seems to get connected to the server but I think there is some problem with the input and output array buffers because there is no output at all. Can someone please guide me how to handle the input and output to and from the server properly to get the desired output?

like image 930
Piyush Avatar asked Apr 29 '11 11:04

Piyush


People also ask

How do I run multiple commands in JSCH?

Executing multiple commands at once on the remote server using Jsch. We must add a semicolon(;) between each command we want to run. So, the format will be first_command;second_command;third_command.

How run multiple Unix commands in Java?

Use && in string to use multiple command.


1 Answers

Avoid using "shell" channel as much as possible. The "shell" channel is intended to implement an interactive session, not to automate a command execution. With "shell" channel, you will face many unwanted side effects.

To automate a command execution, use "exec" channel.


Usually, you can open as many "exec" channels as you need, using each to execute one of your commands. You can open the channels in sequence or even in parallel.

For a complete example of "exec" channel use, see JSch Exec.java example.

This way each command executes in an isolated environment. What may be an advantage, but it can also be undesirable in some cases.


If you need to execute commands in a way that previous commands affect later commands (like changing a working directory or setting an environment variable), you have to execute all commands in the same channel. Use an appropriate construct of the server's shell for that. On most systems you can use semicolons:

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

On *nix servers, you can also use && to make the following commands be executed only when the previous commands succeeded:

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

See also Execute a list of commands from an ArrayList using JSch exec in Java.

In many cases, you do not even need to use multiple commands.

For example, instead of this sequence, that you might do when using shell interactively:

cd /path
ls

You can do:

ls /path

Most complicated situation is, when the commands depend on one another and you need to process results of previous commands before proceeding to other commands.

When you have such need, it usually indicates a bad design. Think hard, if this is really the only solution to your problem. Or consider implementing a server-side shell script to implement the logic, instead of doing it remotely from Java code. Shell scripts have much more powerful techniques to check for results of previous commands, then you have with SSH interface in JSch.

Anyway, see JSch Shell channel execute commands one by one testing result before proceeding.


Side note: Do not use StrictHostKeyChecking=no. See JSch SFTP security with session.setConfig("StrictHostKeyChecking", "no");.

like image 146
Martin Prikryl Avatar answered Sep 20 '22 08:09

Martin Prikryl