我试图通过连接jSch0.1.49库在unix服务器上运行命令。我已经浏览了jSch甚至http://sourceforge.net/apps/mediawiki/jsch/index.php?title=Official_examples
提供的示例我能够从服务器读取响应并将其打印到控制台,但循环是* 永远不会结束 * g。我怀疑为什么Channele在完成从服务器读取响应后没有关闭。
while (true) {
while (inputStream.available() > 0) {
int i = inputStream.read(buffer, 0, 1024);
if (i < 0) {
break;
}
System.out.print(new String(buffer, 0, i));//It is printing the response to console
}
System.out.println("done");// It is printing continuously infinite times
if (channel.isClosed()) {//It is never closed
System.out.println("exit-status: " + channel.getExitStatus());
break;
}
try{Thread.sleep(1000);}catch(Exception ee){}
}
答案 0 :(得分:2)
当没有输入时,通道不会自行关闭。阅读完所有数据后,请尝试自行关闭它。
while (true) {
while (inputStream.available() > 0) {
int i = inputStream.read(buffer, 0, 1024);
if (i < 0) {
break;
}
System.out.print(new String(buffer, 0, i));//It is printing the response to console
}
System.out.println("done");
channel.close(); // this closes the jsch channel
if (channel.isClosed()) {
System.out.println("exit-status: " + channel.getExitStatus());
break;
}
try{Thread.sleep(1000);}catch(Exception ee){}
}
当您使用来自用户的交互式键盘输入时,您唯一一次使用不会手动关闭通道的循环。然后,当用户执行“退出”时,将更改频道的'getExitStatus'。如果你的循环是while(channel.getExitStatus()== -1),那么当用户退出时循环将退出。 检测到退出状态后,您仍需要自行断开频道和会话。
它没有在他们的示例页面上列出,但JSCH在其站点上托管了一个交互式键盘演示。 http://www.jcraft.com/jsch/examples/UserAuthKI.java
甚至他们的演示,我曾经连接到AIX系统而不改变他们的任何代码......当你退出shell时它不会关闭!
在我的远程会话中键入“exit”后,我必须添加以下代码才能使其正常退出:
channel.connect();
// My added code begins here
while (channel.getExitStatus() == -1){
try{Thread.sleep(1000);}catch(Exception e){System.out.println(e);}
}
channel.disconnect();
session.disconnect();
// My Added code ends here
}
catch(Exception e){
System.out.println(e);
}
}
答案 1 :(得分:2)
我认为比轮询退出状态更好的方法是等待Channel.thread结束
Thread t = channel.getThread();
if(t != null)
{
synchronized(t){
t.wait();
}
System.out.println("Channel thread completed, disconnecting session");
session.disconnect();
}
我已经向Channel添加了一个getThread()成员,它只返回当前的通道线程,我也修改了Channel.disconnect(),这样如果线程成员,则线程成员不会设置为零还活着
if(thread != null)
{
if(!thread.isAlive())
thread = null;
}
而不是
thread = null;
在Channel.disconnect()中
答案 2 :(得分:1)
我发现上面给出的解决方案会挂起。我的解决方案删除了&#34; while(true)&#34;,选择了更直接的方法。
private void sendCommand(Channel channel, String command) {
try {
//
this.channelExec = (ChannelExec) channel;
this.channelExec.setCommand(command);
//channel.setInputStream(null);
channel.setOutputStream(System.out);
this.is = channel.getInputStream();
channel.connect();
byte[] buffer = new byte[1024];
while (channel.getExitStatus() == -1) {
while (is.available() > 0) {
int i = is.read(buffer, 0, 1024);
// System.out.println("i= " + i);
if (i < 0) {
// System.out.println("breaking");
break;
}
String string = new String(buffer, 0, i);
output = output.concat(string);
//System.out.println("String= " + string);
}
if (channel.isClosed()) {
//System.out.println("exit-status: " + channel.getExitStatus());
break;
}
}
is.close();
channel.disconnect();
this.session.disconnect();
System.out.println("Done");
} catch (IOException ex) {
System.out.println("ERROR: " + ex);
Logger.getLogger(SSH.class.getName()).log(Level.SEVERE, null, ex);
} catch (JSchException ex) {
System.out.println("ERROR: " + ex);
Logger.getLogger(SSH.class.getName()).log(Level.SEVERE, null, ex);
}
}
答案 3 :(得分:1)
我还尝试使用jSch远程执行多个命令并获取命令输出到控制台(标准输出System.out
)。还想等到所有命令在远程机器上完全执行。
经过多次摆弄和谷歌搜索几个小时后,我能够提出以下建议:
import java.io.InputStream;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.Session;
public class SSHUtility
{
public static void main(String[] args)
{
//Separate multiple commands by semicolon
//Do not separate commands with `exit` command, otherwise
//commands following `exit` will not execute
String commands = "source ~/Downloads/helloworld.sh;echo Mahesha999";
executeCommand("username", "password", "hostname", 22, commands);
System.out.println("done");
}
public static void executeCommand(String pUser, String pPassword, String pServer, int pPort, String pCommand)
{
System.out.println("Executing on ssh");
JSch lJSCH;
Session lSession;
lJSCH = new JSch();
try
{
lSession = lJSCH.getSession(pUser, pServer, pPort);
lSession.setConfig("StrictHostKeyChecking", "no");
lSession.setPassword(pPassword);
System.out.println("Connecting session...");
lSession.connect();
System.out.println("Session connected.");
ChannelExec lChannelExec = (ChannelExec)lSession.openChannel("exec");
lChannelExec.setCommand(pCommand);
((ChannelExec)lChannelExec).setErrStream(System.err);
InputStream ins=lChannelExec.getInputStream();
System.out.println("Connecting exec channel...");
lChannelExec.connect();
System.out.println("exec channel connected.");
byte[] tmp=new byte[1024];
System.out.println("Trying to read remote command output...");
while(true)
{
while(ins.available()>0)
{
int i=ins.read(tmp, 0, 1024);
if(i<0)break;
System.out.print(new String(tmp, 0, i));
}
if(lChannelExec.isClosed())
{
if(ins.available()>0) continue;
System.out.println("exit-status: "+lChannelExec.getExitStatus());
break;
}
try{Thread.sleep(1000);}catch(Exception ee){}
}
lChannelExec.disconnect();
lSession.disconnect();
}
catch(Exception e)
{
System.out.println(e);
}
}
}
/*
Output:
Executing on ssh
Connecting session...
Session connected.
Connecting exec channel...
exec channel connected.
Trying to read remote command output...
Hello
Mahesha999
exit-status: 0
done
*/
答案 4 :(得分:1)
如果您的命令格式正确,我发现只要您及时读取输出,Jsch将在命令完成后关闭通道。在JCraft示例中,channel.isClosed()是他们检查命令完成的唯一内容。他们在等待通道关闭的同一线程中读取输出。更好的方法是创建一个单独的线程来读取输出。示例如下:
Jsch代码:
Channel channel = null;
int exitStatus = 0;
List<String> lines = new ArrayList<String>();
try {
channel = session.openChannel("exec");
((ChannelExec) channel).setCommand(command);
// Read output in separate thread
Thread stdoutReader = new InputStreamHandler(channel.getInputStream(), "STDOUT", lines);
// Run the command
((ChannelExec)channel).setCommand(command);
channel.connect();
// Start thread that reads output
stdoutReader.start();
// Poll for closed status
boolean channelClosed = channel.isClosed();
exitStatus = channel.getExitStatus();
boolean stdOutReaderAlive = stdoutReader.isAlive();
int loopCounter = 0;
while (!channelClosed) {
if (loopCounter % 60 == 0) {
log.info("SSH command '" + command + "' still in while loop checking for after " + (loopCounter/60) + " mins.");
}
loopCounter++;
Thread.sleep(1000);
channelClosed = channel.isClosed();
exitStatus = channel.getExitStatus();
stdOutReaderAlive = stdoutReader.isAlive();
}
log.info("SSH command '" + command + "' exited while loop with values: channelClosed=" + channelClosed + ", exitStatus=" + exitStatus + ", stdOutReaderAlive=" + stdOutReaderAlive);
// finish reading output
stdoutReader.join();
exitStatus = channel.getExitStatus();
log.info("SSH command '" + command + "' final exitStatus=" + exitStatus);
for (String line : lines) {
log.info("SSH output: " + line);
}
} catch (Exception e) {
throw new RuntimeException("Error occured processing SSH request. See nested exception for details.", e);
} finally {
// Always try to close the channel and session.
try {
channel.disconnect();
} catch(Exception e) {
this.log.error("Error - disconnecting channel", e);
}
try {
session.disconnect();
} catch(Exception e) {
this.log.error("Error - disconnecting session", e);
}
}
InputStreamHandler:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* A lot of debate on how to stop a thread... going with the method given here: http://www.javaspecialists.eu/archive/Issue056.html
*/
public class InputStreamHandler extends Thread {
protected Log logger = LogFactory.getLog(getClass());
private InputStream is = null;
private String type = null;
private StringBuilder buffer;
private List<String> lines;
public InputStreamHandler(InputStream is, String type) {
this.is = is;
this.type = type;
}
public InputStreamHandler(InputStream is, String type, StringBuilder buffer) {
this(is, type);
this.buffer = buffer;
}
public InputStreamHandler(InputStream is, String type, List<String> lines) {
this(is, type);
this.lines = lines;
}
public void run() {
try {
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
if (buffer != null) {
String line = null;
while ((line = br.readLine()) != null) {
buffer.append(line + "\n");
}
} else if (lines != null) {
String line = null;
while ((line = br.readLine()) != null) {
lines.add(line);
}
} else {
// just consume output
while (br.readLine() != null)
;
}
} catch (InterruptedIOException ioe) {
// when exception is thrown the interrupt flag is set to false... this will set it back to true
Thread.currentThread().interrupt();
} catch (IOException ioe) {
if (!isInterrupted()) {
throw new RuntimeException("Caught IQException for "
+ this.type + " " + this.getClass().getName() + ".",
ioe);
}
} finally {
closeInputStream();
}
}
@Override
public void interrupt() {
super.interrupt();
closeInputStream();
logger.info(this.type + " " + this.getClass().getName()
+ " thread was interrupted.");
}
private void closeInputStream() {
try {
is.close();
} catch (Exception e) {
}
}
}
答案 5 :(得分:0)
经过多次尝试和错误,我发现此问题的最佳解决方案是在命令/命令列表的末尾添加“退出”命令:
String[] commands = {
"sudo yum update -y",
"sudo yum install java-1.8.0-openjdk-devel -y",
"exit"};
String commandsList = String.join(";", commands);
Channel channel = session.openChannel("shell");
OutputStream ops = channel.getOutputStream();
PrintStream ps = new PrintStream(ops, true);
channel.connect();
ps.println(commandsList);
InputStream input = channel.getInputStream();
BufferedReader reader = null;
String line;
reader = new BufferedReader(new InputStreamReader(input));
while ((line = reader.readLine()) != null) {
line = reader.readLine();
System.out.println(line);
}