Getting Unwanted Characters When Reading Command Output from Ssh Server Using Jsch

Getting unwanted characters when reading command output from SSH server using JSch

These are ANSI escape codes that are normally interpreted by a terminal client to pretty [color] print the output.

If the server is correctly configured, you get these only, when you use an interactive terminal. In other words, if you requested a pseudo terminal for the session (what you should not, if you are automating the session).

The JSch automatically requests the pseudo terminal, if you used a "shell" channel, as that is supposed to be used for implementing an interactive terminal.

  • If you automate an execution of remote commands, you better use an "exec" channel, as shown in JSch Exec.java example.

  • Alternatively, you can prevent JSch from requesting the pseudo terminal by calling setPty. But I do not recommend using "shell" channel. Though if you need to, for whatever reason, you should call setPty(false) in any case, as that will prevent many other similar troubles. I actually already recommended that to you in your previous question.


Note to others: While I see why OP uses ls command, in general, one should use use SFTP API to retrieve a directory listing, instead of executing ls and parsing its output. Parsing ls output is a pretty unreliable approach.


Related questions:

  • Removing shell stuff (like prompts) from command output in JSch
  • Is there a simple way to get rid of junk values that come when you SSH using Python's Paramiko library and fetch output from CLI of a remote machine?

Escape Codes/Random characters in JSch ssh response

I am using exec channel and there are prompts as well. Added a regex to replace escape codes and it is now readable.

text.replaceAll("(\\x9B|\\x1B\\[)[0-?]*[ -\\/]*[@-~]", "");

Executing multiple commands over SSH exec channel on firewall device with Java JSch does not work

The "exec" channel on your device seems to be implemented incorrectly. So you cannot use your code with the "exec" channel. As you can "ssh" to the device, it seems that the "shell" channel is fully working. Try talking to your server administrator, to get the server fixed.

If fixing the server is not feasible, you will have to revert to using the "shell" channel, although it is generally not the correct way to implement command automation.

See What is the difference between the 'shell' channel and the 'exec' channel in JSch

JSch by default enables terminal emulation for the "shell" channel, what will bring lot of unwanted side effects (see Getting unwanted characters when reading command output from SSH server using JSch). You may need to disable that by calling setPty.

ChannelShell channel = (ChannelShell) session.openChannel("shell"); 

InputStream in = channel.getInputStream();
outStream = channel.getOutputStream();

channel.setPty(false);
channel.connect();

outStream.write('configure\n'.getBytes());
outStream.write('move shared pre-rulebase security rules TEST top\n'.getBytes());

How to get the Output result in jsch

this is how I read the output of my command

Edit

1) Method to Connect to the server:

public void connect (final String host){
if(host.isEmpty())
return;

hostname = host;
try{
JSch jsch=new JSch();
String user ="yourUserName";
String host = "yourHost";

Session myLocalSession=jsch.getSession(user, host, 22);
//myLocalSession=jsch.getSession(user, "192.168.1.104", 22);

myLocalSession.setPassword("yourPassword");

myLocalSession.setConfig("StrictHostKeyChecking", "no");

myLocalSession.connect(5000); // making a connection with timeout.
myChannel = myLocalSession.openChannel("shell");

InputStream inStream = myChannel.getInputStream();

OutputStream outStream = myChannel.getOutputStream();
toChannel = new PrintWriter(new OutputStreamWriter(outStream), true);

myChannel.connect();
readerThread(new InputStreamReader(inStream));


Thread.sleep(100);
sendCommand("cd "+path);
}
catch(JSchException e){
String message = e.getMessage();
if(message.contains("UnknownHostException"))
myParser.print(">>>>> Unknow Host. Please verify hostname.");
else if(message.contains("socket is not established"))
myParser.print(">>>>> Can't connect to the server for the moment.");
else if(message.contains("Auth fail"))
myParser.print(">>>>> Please verify login and password");
else if(message.contains("Connection refused"))
myParser.print(">>>>> The server refused the connection");
else
System.out.println("*******Unknown ERROR********");

System.out.println(e.getMessage());
System.out.println(e + "****connect()");
}
catch(IOException e)
{
System.out.println(e);
myParser.print(">>>>> Error when reading data streams from the server");
}
catch(Exception e){
e.printStackTrace();
}
}

2) Method to send a command to the server

public void sendCommand(final String command)
{
if(myLocalSession != null && myLocalSession.isConnected())
{
try {
toChannel.println(command);
} catch(Exception e){
e.printStackTrace();
}
}
}

3) Thread method that read answer from the server

void readerThread(final InputStreamReader tout)
{
Thread read2 = new Thread(){
@Override
public void run(){
StringBuilder line = new StringBuilder();
char toAppend = ' ';
try {
while(true){
try {
while (tout.ready()) {
toAppend = (char) tout.read();
if(toAppend == '\n')
{
System.out.print(line.toString());
line.setLength(0);
}
else
line.append(toAppend);
}
} catch (Exception e) {
e.printStackTrace();
System.out.println("\n\n\n************errorrrrrrr reading character**********\n\n\n");
}
Thread.sleep(1000);
}
}catch (Exception ex) {
System.out.println(ex);
try{
tout.close();
}
catch(Exception e)
{}
}
}
};
read2.start();
}

You can use a bufferedReader with the InputStreamReader and read line by line. I use an infinite loop and pause for one second after each failed attempt to read (nothing from the server).

Let's say that the three method are in SessionB class. Example:

SessionB testConnexion = new SessionB();
testConnexion.connect();
testConnexion.sendCommand("cd myFolder");
testConnexion.sendCommand("ls");

You should get the list of file in your console.

If you need, to be able to interact (send a command depending of the output), check my post here.

How to set Java home in JSch to execute a command?

ChannelExec.setEnv on JSch side probably works. But assuming you are connecting to an OpenSSH server, the server must be explicitly configured to allow you to set the LANG and JAVA_HOME environment variables using the AcceptEnv directive. What it probably is not.

You can try the same using PuTTY (Connection > Data > Environment variables). You will probably get "Server refused to set environment variables" message back.

See also How can I set environment variables when I ssh login to my Unix box by passing custom arguments?


Anyway, the correct solution is to fix your server configuration to set the environment correctly even for non-interactive sessions.

Or as a dirty workaround you can set the variables directly in your command:

JAVA_HOME=/blah/java/J8.0_64/; java ...

See also Certain Unix commands fail with "... not found", when executed through Java using JSch.

Using specific command output of JSch to trigger next command

I wrote this awhile ago, but I did it a little differently then how you have it setup. It grabs the returns how you would like and all that though.

Edit: I updated the code, so the username, host, and password can easily be set pragmatically. It then loops through an array of commands, checking the return with the ability to execute another command or continue on.

Here's the code with a few extra comments (hopefully helpful)

import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import java.io.InputStream;
import java.util.Scanner;
import java.util.Properties;

public class cmdExec
{
public static void main(String[] arg)
{
try
{
JSch jsch = new JSch();

String user = "<username>";
String host = "<host>";
String paswrd = "<password>";

Session session = jsch.getSession(user, host, 22);

Properties config = new Properties();
config.put("StrictHostKeyChecking", "no");

session.setPassword(paswrd);

session.setConfig(config);
session.connect();

// build an array of commands to run
String[] cmds = {"user | wc -l", "ps -ef | wc -l"};

for(int cnt = 0; cnt < cmds.length; cnt++)
{
String command = cmds[cnt];

Channel channel = session.openChannel("exec");
((ChannelExec)channel).setCommand(command);

channel.setInputStream(null);

((ChannelExec)channel).setErrStream(System.err);

InputStream in = channel.getInputStream();

channel.connect();

byte[] tmp = new byte[1024];

while (true){

while (in.available()>0){
int i = in.read(tmp, 0, 1024);
if (i < 0)
{
break;
}
// grab the return, hopefully a number
String strRet = new String(tmp, 0, i);
// trim whitespace and convert to int
int ret = Integer.parseInt(strRet.trim());

if ( ret != 1 )
{
System.out.println("Do something else, the return wasn't one.");
}
else
{
System.out.println("Return was 1. Continuing on...");
}
}
if(channel.isClosed()){
if(in.available()>0) continue;
//System.out.println("exit-status: "+channel.getExitStatus());
break;
}
try
{
Thread.sleep(1000);
}
catch (Exception ee)
{
// Do something with the exception
}
}
channel.disconnect();

}
}
catch(Exception e){
System.out.println(e);
}
}
}

JSch: Is there a way to expose user environment variables to exec channel?

Since you're not opening up an interactive shell, your environment variables won't be set. However, you can use the bash command with --login (man bash for more details) to get the results you want

bash --login -c 'command arg1 ...'"


Related Topics



Leave a reply



Submit