Executing Commands

Lee Painter

In Creating an SSH Client we demonstrated how to connect the SshClient object and authenticate the user. One of the many features SSH provides is the ability to execute commands on the remote server. This article outlines the options available for executing commands.

When we talk about executing a command over SSH we are speaking specifically about the execution of a single command line, in a single session. This is not the same as starting a shell and executing commands interactively. Executing a command using the following techniques is equivalent to the following ssh command line operation:

ssh lee@localhost ls -l

In this scenario, the command is executed and the session on the server lasts for the duration of the remote process. Once the process has completed execution the session is closed and can no longer be used for any other command execution. If you need to execute further commands you need to repeat the operation by opening new sessions and executing those commands separately. However, this method is not suitable for executing interactive commands that require the state to be passed between them. For example, you could not "cd tmp" and then "touch foo.txt" to create a file in tmp because the session state is lost between the first and second command executions. If you want to perform operations like this, you need to start a shell or execute the commands on the same command line, for example, "cd tmp && touch foo.txt".

There are two methods of executing commands with the SshClient. First, we can use the helper methods on SshClient itself.

try(SshClient ssh = new SshClient("localhost", 22, "lee", "xxxxx".toCharArray()) {
System.out.println(ssh.executeCommand("ls -l"));
}


This example returns the output from the command as a String. There are additional options to pass to this method. If we want to place a time limit on the command execution, we can pass a timeout:

ssh.executeCommand("find . | grep foo", 60000L);


Note that this is not a socket timeout if the command fails to complete without 60000ms then an exception will be raised. 

You can also pass the character set that you are expecting in the returned String:

ssh.executeCommand("find . | grep foo", "ISO-8859-1");


The default character set is UTF-8. You can also combine all 3 options into a single call:

ssh.executeCommand("find . | grep foo", 60000L, "ISO-8859-1");


If you need to capture the exit code of the command, then the executeCommandWithResult methods are available with options for all the above same parameters.

int result = executeCommandWithResult("ls -l); 


Note, that it's not a requirement for the server to send the exit code. You should always check the constant SshClient.EXIT_CODE_NOT_RECEIVED before you check the result value.

Using AbstractCommandTask

If you require more flexibility in the processing command output, for example, if you want to work directly with the session's InputStream you should use the AbstractCommandTask to execute the commands. Let's take a look at how we can create an AbstractCommandTask to read from the stream.

AbstractCommandTask task = new AbstractCommandTask(ssh.getConnection(), "ls -l") {
   protected void onOpenSession(SessionChannelNG session) throws IOException {
      int r;
      while((r = session.getInputStream().read()) > -1) {
            ...
      }
   }
};


We can then run the task passing it to the SshClient, passing an optional timeout if we want.

ssh.runTask(task, 60000L);

 

The above will run the task until it completes. You can also post it and not wait for completion using:

ssh.addTask(task);


If you need to block for completion later you can use the task's future interface:

task.waitForever();

// or

task.waitFor(60000L);

 

You can check the exit code on the task after the method returns

int result = task.getExitCode();

 

Using this method, you can also interact with the command. For example, if you know the process is going to prompt for user input, you can send it by writing data to the session's OutputStream:

session.getOutputStream().write("y".getBytes());


You may want to review the further documentation on Executing Commands within an Interactive Shell. This describes the techniques available to interact with an SSH shell and execute commands interactively to mimic user input in a terminal.