通过 JSch shell 通道向服务器发送命令
Posted
技术标签:
【中文标题】通过 JSch shell 通道向服务器发送命令【英文标题】:Sending commands to server via JSch shell channel 【发布时间】:2011-05-10 19:34:07 【问题描述】:我不知道如何通过 JSch shell 通道发送命令。
我这样做了,但它不起作用:
JSch shell = new JSch();
String command = "cd home/s/src";
Session session = shell.getSession(username, host, port);
MyUserInfo ui = new MyUserInfo();
ui.setPassword(password);
session.setUserInfo(ui);
session.connect();
channel = session.openChannel("shell");
fromServer = new BufferedReader(new InputStreamReader(channel.getInputStream()));
toServer = channel.getOutputStream();
channel.connect();
toServer.write((command + "\r\n").getBytes());
toServer.flush();
然后我读到这样的输入:
StringBuilder builder = new StringBuilder();
int count = 0;
String line = "";
while(line != null)
line = fromServer.readLine();
builder.append(line).append("\n");
if (line.endsWith(".") || line.endsWith(">"))
break;
String result = builder.toString();
ConsoleOut.println(result);
【问题讨论】:
错误信息是什么?有堆栈跟踪吗? 没有错误信息,我只是没有收到任何信息。程序停在line = fromServer.readLine(),巫婆的意思是消息还没有发送……至少,我是这么认为的…… 希望对您有帮助:jcraft.com/jsch/examples :) 我正在按照这些示例工作... Thnx anw :) jcraft.com/jsch/examples/Shell.java 的示例真的很好用。它只是设置 System.in 和 System.out,它们在管道流和控制台之间找到它们的方式。但是当我通过 ssh 连接到远程主机时,BufferedReader.readLine() 永远不会返回。解决方法是读取大块字节而不是字符串,然后手动从它们构造字符串。 【参考方案1】:试试这个代码:
JSch jsch=new JSch();
System.out.println("Getting session");
Session session=jsch.getSession("root","10.0.0.0",22);
System.out.println("session is ::::"+session.getHost());
// username and password will be given via UserInfo interface.
UserInfo ui = new MyUserInfo("Lab@123", null);
//UserInfo ui = new MyUserInfo(password, null);
session.setUserInfo(ui);
session.setPassword("Lab@123");
Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect(40000);
Channel channel=session.openChannel("exec");
((ChannelExec)channel).setCommand("ls");
channel.connect();
channel.run();
// get I/O streams for remote scp
OutputStream out=channel.getOutputStream();
InputStream in=channel.getInputStream();
String output="";
while (channel.isClosed()!=true)
try
output+=streamToString(in);
catch (Exception e)
// TODO Auto-generated catch block
e.printStackTrace();
System.out.println("Output is :::"+output);
channel.disconnect();
session.disconnect();
public static String streamToString(InputStream input)throws Exception
String output = ""; while(input.available()>0) output += ((char)(input.read())); return output;
public static OutputStream stringToStream(String charset) throws IOException
byte[] bytes = charset.getBytes();
/*ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
InputStreamReader isr = new InputStreamReader(bais);*/
InputStream is = null;
OutputStream os = null;
try
is = new ByteArrayInputStream(charset.getBytes("UTF-8"));
catch (UnsupportedEncodingException e)
// TODO Auto-generated catch block
e.printStackTrace();
//byte[] buf = new byte[1024];
int numRead;
while ( (numRead = is.read(bytes) ) >= 0)
os.write(bytes, 0, numRead);
return os;
【讨论】:
API 说 channel.run() 不应该被调用:here【参考方案2】:如果它挂在readLine()
,这意味着您的“while”永远不会结束(考虑到您的代码可能不太可能),或者,readLine()
正在等待它的源,即iostream
阻塞线程导致@ 987654324@。
如果没有看到您的调试信息,我无法对您的代码进行故障排除。但作为一个建议,你试过PipedIntputStream
吗?这个想法是将您的控制台输入通过管道传输到“您的”输出,以便您可以“编写”它。要实现这一点,您需要初始化输入/输出。
InputStream in = new PipedInputStream();
PipedOutputStream pin = new PipedOutputStream((PipedInputStream) in);
/**...*/
channel.setInputStream(in);
channel.connect();
/** ...*/
pin.write(myScript.getBytes());
您的问题也是如此,如何读取控制台输出。
PipedInputStream pout = new PipedInputStream((PipedOutputStream) out);
/**
* ...
*/
BufferedReader consoleOutput = new BufferedReader(new InputStreamReader(pout));
consoleOutput.readLine();
再一次,如果您不确定要阅读多少行并因此想使用“while”,请确保在 while 中执行某些操作以防止 1)忙等待 2)结束条件。示例:
while(!end)
consoleOutput.mark(32);
if (consoleOutput.read()==0x03) end = true;//End of Text
else
consoleOutput.reset();
consoleOutput.readLine();
end = false;
【讨论】:
最后一部分没看懂。位置“32”是任意的吗?是否可以在当前位置设置标记并稍后重置? @Kariem 32 是 32 个字节作为标记缓冲区。已经好几年了,所以我不记得为什么我使用 32 而不是 4。4 应该足够大以容纳一个read()
但也许我的理由是设置更大并不会损害逻辑。【参考方案3】:
试试这个:
JSch jsch = new JSch();
try
Session session = jsch.getSession("root", "192.168.0.1", 22);
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
String command = "lsof -i :80";
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;
System.out.print(new String(tmp, 0, i));
if (channel.isClosed())
System.out.println("exit-status: " + channel.getExitStatus());
break;
try
Thread.sleep(1000);
catch (Exception ee)
channel.disconnect();
session.disconnect();
catch (Exception e)
System.out.println(e.getMessage());
【讨论】:
关于if (i < 0) break;
这一行,在确保输入流有可用数据后,读取这个数据会不会返回一个负数表示读取的数据量?!为什么它首先表明它有可用的数据?!
此代码有时会在第一个 while 循环内的 rpm 安装中挂起。【参考方案4】:
以下是我的作业快速编写的代码。不是一个做得好的程序。 但服务于它的目的。
-
通过 SSH(使用 Jsch)连接到服务器(使用私钥文件 - mykey.pem)
创建一个 shell 脚本(挂载一个卷和 mkfs)
在远程机器上运行
您始终可以在标准输出上看到输出
代码如下:
public class connectSSH
public void connect(String dnsName, String privKey) throws IOException
JSch jSch = new JSch();
try
//Authenticate through Private Key File
jSch.addIdentity(privKey);
//Give the user and dnsName
Session session = jSch.getSession("root", dnsName, 22);
//Required if not a trusted host
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
System.out.println("Connecting SSH to " + dnsName + " - Please wait for few minutes... ");
session.connect();
//Open a shell
Channel channel=session.openChannel("shell");
channel.setOutputStream(System.out);
//Create a Shell Script
File shellScript = createShellScript();
//Convert the shell script to byte stream
FileInputStream fin = new FileInputStream(shellScript);
byte fileContent[] = new byte[(int)shellScript.length()];
fin.read(fileContent);
InputStream in = new ByteArrayInputStream(fileContent);
//Set the shell script to the channel as input stream
channel.setInputStream(in);
//Connect and have fun!
channel.connect();
catch (JSchException e)
// TODO Auto-generated catch block
e.printStackTrace();
public File createShellScript()
String filename = "shellscript.sh";
File fstream = new File(filename);
try
// Create file
PrintStream out = new PrintStream(new FileOutputStream(fstream));
out.println("#!/bin/bash");
out.println("echo \"hi\" > /tmp/test.info");
out.println("echo \"n\" > /tmp/fdisk.in");
out.println("echo \"p\" >> /tmp/fdisk.in");
out.println("echo \"1\" >> /tmp/fdisk.in");
out.println("echo >> /tmp/fdisk.in");
out.println("echo >> /tmp/fdisk.in");
out.println("echo \"w\" >> /tmp/fdisk.in");
out.println("/sbin/fdisk /dev/sdf < /tmp/fdisk.in");
out.println("mkfs.ext3 /dev/sdf1");
out.println("mkdir /usr/myebs");
out.println("mount /dev/sdf1 /usr/myebs");
out.println("partprobe /dev/sdf1");
out.println("echo \"Success\"");
//Close the output stream
out.close();
catch (Exception e)//Catch exception if any
System.err.println("Error: " + e.getMessage());
return fstream;
public static void main(String[] args)
connectSSH ssh = new connectSSH();
String privKey = "/Users/neo/Desktop/mykey.pem";
try
ssh.connect("yourexampleserver.com", privKey);
catch (IOException e)
// TODO Auto-generated catch block
e.printStackTrace();
【讨论】:
【参考方案5】:使用管道输入和输出流看起来很有趣:
JSch jsch = new JSch();
jsch.addIdentity("/home/audrius/.ssh/blablabla", "blablablabla");
String user = "audrius";
String host = "ultrastudio.org";
Session session = jsch.getSession(user, host, 439);
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
Channel channel = session.openChannel("shell");
PipedInputStream pip = new PipedInputStream(40);
channel.setInputStream(pip);
PipedOutputStream pop = new PipedOutputStream(pip);
PrintStream print = new PrintStream(pop);
channel.setOutputStream(System.out);
print.println("ls");
【讨论】:
【参考方案6】:试试这个
Channel channel=session.openChannel("shell");
OutputStream ops = channel.getOutputStream();
PrintStream ps = new PrintStream(ops, true);
channel.connect();
ps.println("mkdir folder");
ps.println("dir");
//give commands to be executed inside println.and can have any no of commands sent.
ps.close();
InputStream in=channel.getInputStream();
byte[] bt=new byte[1024];
while(true)
while(in.available()>0)
int i=in.read(bt, 0, 1024);
if(i<0)
break;
String str=new String(bt, 0, i);
//displays the output of the command executed.
System.out.print(str);
if(channel.isClosed())
break;
Thread.sleep(1000);
channel.disconnect();
session.disconnect();
【讨论】:
【参考方案7】:private void executeRemoteCommandAsSudo(String sudoAs, String password,
String command, int delayInSeconds)
logger.info("executeRemoteCommandAsSudo started....");
logger.info("sudoAs=" + sudoAs);
logger.info("command=" + command);
logger.info("delayInSeconds=" + delayInSeconds);
Session session = null;
Channel channel = null;
try
session = getSession();
channel = session.openChannel("exec");
String sudoCommand = "sudo su - " + sudoAs;
((ChannelExec) channel).setCommand(sudoCommand);
((ChannelExec) channel).setPty(true);
channel.connect();
InputStream inputStream = channel.getInputStream();
OutputStream out = channel.getOutputStream();
((ChannelExec) channel).setErrStream(System.err);
out.write((password + "\n").getBytes());
out.flush();
Thread.sleep(1000);
out.write((command + "\n").getBytes());
out.flush();
Thread.sleep(1000 * delayInSeconds);
out.write(("logout" + "\n").getBytes());
out.flush();
Thread.sleep(1000);
logInfo(channel, inputStream);
out.write(("exit" + "\n").getBytes());
out.flush();
out.close();
Thread.sleep(1000);
catch (Exception ex)
logger.error(ex.getMessage());
finally
session.disconnect();
channel.disconnect();
logger.info("executeRemoteCommandAsSudo completed....");
private void logInfo(Channel channel, InputStream in)
try
byte[] tmp = new byte[1024];
while (true)
while (in.available() > 0)
int i = in.read(tmp, 0, 1024);
if (i < 0)
break;
logger.info(new String(tmp, 0, i));
if (channel.isClosed())
logger.info("exit-status: " + channel.getExitStatus());
break;
catch (Exception ex)
logger.error(ex);
private Session getSession() throws JSchException
JSch jsch = new JSch();
logger.info("ftpUser=" + ftpUser);
logger.info("ftpHost=" + ftpHost);
Session session = jsch.getSession(ftpUser, ftpHost, 22);
session.setPassword(ftpPassword);
java.util.Properties config = new java.util.Properties();
config.put("StrictHostKeyChecking", "no");
session.setConfig(config);
session.connect();
return session;
【讨论】:
【参考方案8】:我意识到这是一个旧线程,但我今天遇到了类似的问题。 这是我的解决方案。
public class ChannelConsole
// ================================================
// static fields
// ================================================
// ================================================
// instance fields
// ================================================
private Session session;
// ================================================
// constructors
// ================================================
public ChannelConsole(Session session)
this.session = session;
// ================================================
// getters and setters
// ================================================
// ================================================
// public methods
// ================================================
public String execute(String command) throws JSchException
command = command.trim() + "\n";
ChannelExec channel = (ChannelExec) this.session.openChannel("exec");
channel.setCommand(command);
ByteArrayOutputStream responseStream = new ByteArrayOutputStream();
channel.setOutputStream(responseStream);
channel.connect();
try
awaitChannelClosure(channel);
catch (InterruptedException e)
// no one cares
String result = responseStream.toString();
closeQuietly(responseStream);
return result;
// ================================================
// private methods
// ================================================
private void awaitChannelClosure(ChannelExec channel) throws InterruptedException
while (channel.isConnected())
Thread.sleep(100);
private static void closeQuietly(Closeable closeable)
if (closeable == null)
return;
try
closeable.close();
catch (IOException ignored)
ignored.printStackTrace();
使用此类,您可以执行以下操作:
shell = new ChannelConsole(this.session);
String result = shell.execute("quota -v; echo; echo \"Disk storage information:\"; df -hk")
【讨论】:
【参考方案9】:用法:
String remoteCommandOutput = exec("ssh://user:pass@host/work/dir/path", "ls -t | head -n1");
String remoteShellOutput = shell("ssh://user:pass@host/work/dir/path", "ls")
shell("ssh://user:pass@host/work/dir/path", "ls", System.out)
shell("ssh://user:pass@host", System.in, System.out);
Implementation
【讨论】:
【参考方案10】:使用 JSch 时,您必须使用 InputStream
从 SSH 客户端到服务器进行通信,并使用 OutputStream
从服务器返回到客户端。这可能不是很直观。
以下示例使用管道流来提供更灵活的 API。
创建一个 JSch 会话 ...
Session session = new JSch().getSession("user", "localhost", port);
session.setPassword("secret");
session.setConfig("StrictHostKeyChecking", "no");
session.connect();
如果你想向一个 shell 发送多个命令,你应该使用ChannelShell
,如下所示:
ChannelShell channel = (ChannelShell) session.openChannel("shell");
PipedInputStream pis = new PipedInputStream();
channel.setInputStream(pis);
PipedOutputStream pos = new PipedOutputStream();
channel.setOutputStream(pos);
channel.connect();
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new PipedOutputStream(pis)));
writer.write("echo Hello World\n");
writer.flush();
BufferedReader reader = new BufferedReader(new InputStreamReader(new PipedInputStream(pos)));
String line = reader.readLine(); // blocking IO
assertEquals("Hello World", line);
在ByteArrayOutputStream
的帮助下,您还可以以非阻塞方式进行通信:
ChannelShell channel = (ChannelShell) session.openChannel("shell");
channel.setInputStream(new ByteArrayInputStream("echo Hello World\n".getBytes()));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
channel.setOutputStream(baos);
channel.connect();
sleep(1000); // needed because of non-blocking IO
String line = baos.toString();
assertEquals("Hello World\n", line);
如果您只想发送一个命令,ChannelExec
就足够了。如您所见,输出流的工作方式与以前相同:
ChannelExec channel = (ChannelExec) session.openChannel("exec");
channel.setCommand("echo Hello World");
PipedOutputStream pos = new PipedOutputStream();
channel.setOutputStream(pos);
channel.connect();
BufferedReader reader = new BufferedReader(new InputStreamReader(new PipedInputStream(pos)));
String line = reader.readLine(); // blocking IO
assertEquals("Hello World", line);
【讨论】:
以上是关于通过 JSch shell 通道向服务器发送命令的主要内容,如果未能解决你的问题,请参考以下文章