问题
In my code I am trying to run some commands on a remote server via SSH. The commands must build on each other, but with a logic behind it. This means something like: When the output of command a contains “retcode 0”, then do command b. Else do command c
I found no way to implement this logic into several “exec” commands. It seems like every “exec” has its own process, so I can’t continue where I was before. And with one “exec” I just can pass a list of commands where all of them will be executed, so no logic there. So, I decided to use “shell” for Jsch. (If there is a way to use exec for it, I would be happy with that)
Based on the example from jcraft, I wrote this code:
import com.jcraft.jsch.*;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
public class Main {
public static void main(String[] args) {
try {
JSch jsch = new JSch();
String user = "sshuser";
String host = "localhost";
Session session = jsch.getSession(user, host, 22);
String passwd = "password";
session.setPassword(passwd);
session.setConfig("StrictHostKeyChecking", "no");
//session.connect();
session.connect(30000); // making a connection with timeout.
Channel channel = session.openChannel("shell");
// Enable agent-forwarding.
((ChannelShell)channel).setAgentForwarding(true);
// Set Streams
channel.setInputStream(System.in);
channel.setOutputStream(System.out);
channel.connect(3 * 1000);
} catch (Exception e) {
System.out.println(e);
}
}
}
Basically, this gives me as a human exact the possibility to do what I want to do. I can enter commands in System.in, the return is printed to System.out. I can read it, decide what I want to do with it and then enter the next command. The command will be executed exact at the point where I was earlier, so everything is fine.
Now I have to find a way to do it via java. I found a way to enter the first command through a fix string:
[...]
InputStream testInput = new ByteArrayInputStream( "dir \n".getBytes("UTF-8") );
// Set Streams
channel.setInputStream(testInput);
[...]
But after that I find no way to send the next one (as a first step even without reading the output).
So, my question is, is there a way to set the System.in via Java code which will directly send this via Jsch (System.setIn() doesn’t work for me) or another way to change the input string on the fly, so that it gets transmitted via Jsch?
Thanks for your time!
回答1:
Thanks to the comments from Martin Prikryl I found a solution. I created a little example with Telnet instead of my real application. The basics are the same and i think it is more helpfull because more people can try it out and play with it when it is not based on a specific software.
import com.jcraft.jsch.*;
import java.io.*;
public class Main {
public static void main(String[] args) {
OutputStream out = null;
Session session = null;
try {
JSch jsch = new JSch();
String user = "sshuser";
String host = "localhost";
session = jsch.getSession(user, host, 22);
String passwd = "password";
session.setPassword(passwd);
session.setConfig("StrictHostKeyChecking", "no");
// vars and objects used later
String lineSeperator = System.getProperty("line.separator");
StringBuilder sb = new StringBuilder();
Main main = new Main();
//session.connect();
session.connect(30000); // making a connection with timeout.
ChannelExec channel = (ChannelExec) session.openChannel("exec");
InputStream in = channel.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
// start telnet session
channel.setCommand("telnet 192.168.222.128 -l sshuser");
out = channel.getOutputStream();
channel.connect();
// wait a little bit for telnet to be ready to take the input
Thread.sleep(500);
// pass the password
out.write(("password\n").getBytes());
out.write(("\n").getBytes());
Thread.sleep(500);
// flush reader, very important!
out.flush();
// Read from Bufferreader until the current line contains a specific string
// For my real application it would be "--- END", for this example i
// used something from the last line my machine returns. Very important that this string
// appears on every possible output, or you will stuck in a while loop!
//
// Tried it with while((reader.readline())!=null) but this ends in a infinity loop too.
// Since in my application there is an String that always get returned i didn't look it further up
String responeFromLogin = main.readOutput("security updates.", reader, lineSeperator, sb);
// Working with the response, in this example a simple fail-->Exception, success --> progress
if (responeFromLogin.contains("Login incorrect")) {
throw new Exception("Failed: Login");
}
System.out.println("Login Successfull");
// Log in was successful, so lets do the next command, basiclly the same routine again
out.write(("dir\n").getBytes());
Thread.sleep(500);
out.flush();
// Again, not bulletproofed in this example
String responseFromHelp = main.readOutput("examples.desktop", reader, lineSeperator, sb);
if (!responseFromHelp.contains("test")) {
throw new Exception("Failed: Help");
}
System.out.println("Folder Found");
} catch (InterruptedException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
} catch (JSchException e1) {
e1.printStackTrace();
} catch (Exception e1) {
e1.printStackTrace();
} finally {
try {
if (out != null) {
out.flush();
}
} catch (IOException e1) {
e1.printStackTrace();
}
System.out.println("_________________________");
System.out.println("I am done");
if (session != null) {
session.disconnect();
}
}
}
public String readOutput(String endString, BufferedReader reader, String lineSeperator, StringBuilder sb) {
String line;
String returnString = "Error";
while (true) {
try {
line = reader.readLine();
if (line.contains(endString)) {
sb.append(line).append(lineSeperator);
returnString = sb.toString();
break;
} else {
sb.append(line).append(lineSeperator);
}
} catch (IOException e) {
returnString = "Error";
e.printStackTrace();
}
}
return returnString;
}
}
来源:https://stackoverflow.com/questions/51987798/change-system-in-and-read-system-out-programmly-on-the-fly-jsch