Executing Single Commands

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 discussing executing a command over SSH, we speak specifically about executing 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 unsuitable for executing interactive commands requiring 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 = SshClientBuilder.create()
	.withHostname("localhost")
	.withPort(2222)
	.withUsername("root")
	.build()) {
    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; an exception will be raised if the command fails to complete without 60000ms. 

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 three options into a single call:

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

If you need to capture the command’s exit code, then the executeCommandWithResult methods are available with options for all the above 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

Suppose 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 look at how to 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 and passing an optional timeout.

ssh.runTask(task, 60000L);

The above will run the task until it is completed. 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 review 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.