I want to be able to send many consecutive command represented as strings within a Java application to a SSH server for execution. Should I use:
Channel cha
With shell channel the shell (on unix it's sh or bash or something like that, on Windows it's usually cmd.exe) is started and console is created (the same you see on the screen if you run them locally). You have a prompt which you can parse or use for detection of completion of command.
With command channel a shell instance is started for each command (actually the channel is opened for each command) and a command is passed as a parameter for the shell (on Windows it would look like "cmd.exe /c ".
It's easier to use command channel cause you don't need to deal with command prompt.
well, I found out that this works and it's really convenient if you want to preserve state like a regular shell would:
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();
You could change
channel.setOutputStream(System.out, true);
with
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");
if you want more control on the output.
=============================================================================
EDITED: follow up
why sometimes the commands I send through the PrintStream appear randomly in the output. i.e. the following code:
shell[0].println("cd ..");
shell[0].println("cd ..");
shell[0].println("ls -la");
shell[0].println("exit");
produces this: (marked with {thing} are things that shouldnt be there!)
Last login: Thu Jul 21 21:49:13 2011 from gateway
Manifests: trunk-latest
[host ~]$ cd ..
{cd ..}[host home]$
[host home]$ cd ..
[host /]$
[host /]$ ls -la
{exit}total 9999
---------- 27 root root 4096 Jan 26 2010 .
---------- 27 root root 4096 Jan 26 2010 ..
---------- 1 root root 0 Mar 14 19:16 .autojyk
---------- 1 root root 0 Feb 9 2009 .automan
---------- 1 root root 3550 May 14 2010 .bash_history
d--------- 2 root root 4096 Apr 26 04:02 put
d--------- 5 root root 4024 Apr 25 19:31 boot
[m[host /]$
[host /]$ exit
logout
An overview about the differences and similarities between these streams you can find at »Shell, Exec or Subsystem Channel« in the JSch wiki. Here some details for your use case.
In the exec channel, the commands come from the command string you did give with setCommand()
. The SSH server will pass them at once to the shell (using something like bash -c '<command>'
).
They will all be executed, if the shell does not somehow exit before for some reason. (You could send a whole shell script here which implements some logic using if
and similar, if this is wanted.)
So, to execute multiple commands, you could pass them to the exec channel by separating them with ;
or newline (\n
). As you can't wait for the results before giving all the commands, here you can only use multiple exec channels (but as each channel spawns a new shell, they don't conserve state between them, like working directory or shell variables).
In the shell channel, the shell will read input from the stream, and interpret the first line as a command (or several ones).
Then it will execute this command. The command itself might read more input from the stream, if it wants.
Then the shell will read the next line, interpret it as a command, and execute.
(In some cases the shell has to read more than one line, for example for long strings or composed commands like if or loops.)
This will go on until either the end of the stream (e.g. stream.close() at your side) or executing an explicit exit command.
If you don't send any input to the shell via the channels input/output stream, the shell will simply wait until you either send more or close the stream. Thus you can quietly read the output of one command, do some calculations on the client side and then decide which command to send next.
Just make sure you don't mix input to one command with the text of the next command - preferably don't use any commands which will read from standard input.
It's not so much about JSch. It's about how the server implements the two channels.
In common *nix OpenSSH server:
The shell
channel executes a login shell (as if you login with SSH terminal client). The shell will then present a command prompt and wait for client/user to type the commands. The purpose of the shell
channel is to implement an interactive shell session. That is something one will do very very rarely. If you do, you typically want to use terminal emulation.
The shell
channel is obviously used by SSH terminal clients (like OpenSSH ssh
or PuTTY), under normal circumstances.
The shell
channel is a black box with an input and output. The input and output have no structure. If you for example execute a command by sending it to the input, you will never be able to tell when it ended. If you send two commands to input queue, you won't be able to distinguish what output comes from what command.
The exec
command takes a command as an "argument" and executes it in an isolated environment – still via user's default shell, but not as a "login" shell, what may cause significant differences in the command execution.
The purpose of the exec
channel is automating a command execution. So typically you do not want to use a terminal emulation, to avoid the command to do fancy stuff like pagination, coloring and mainly interactive confirmations.
exec
channel is used by OpenSSH ssh
or PuTTY plink
, when you specify a command to execute on its command line:
ssh user@host command
With less common SSH servers, the difference can be even more significant. Some servers may even not support one of the channels. It is also quite common that they seemingly support both, but one of them (typically the exec
) is completely broken.
There's a similar question for Python/Paramiko:
What is the difference between exec_command and send with invoke_shell() on Paramiko?