我試圖通過SSH與JSch運行命令,但JSch幾乎沒有任何文檔,我發現的例子很糟糕。例如,this one不顯示處理輸出流的代碼。並且,this one使用醜陋的黑客知道何時停止從輸出流中讀取數據。使用JSch通過SSH運行命令
回答
以下用Java編寫的代碼示例將允許您在一個java程序中通過SSH在外部計算機上執行任何命令。您將需要包含com.jcraft.jsch jar文件。
/*
* SSHManager
*
* @author cabbott
* @version 1.0
*/
package cabbott.net;
import com.jcraft.jsch.*;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
public class SSHManager
{
private static final Logger LOGGER =
Logger.getLogger(SSHManager.class.getName());
private JSch jschSSHChannel;
private String strUserName;
private String strConnectionIP;
private int intConnectionPort;
private String strPassword;
private Session sesConnection;
private int intTimeOut;
private void doCommonConstructorActions(String userName,
String password, String connectionIP, String knownHostsFileName)
{
jschSSHChannel = new JSch();
try
{
jschSSHChannel.setKnownHosts(knownHostsFileName);
}
catch(JSchException jschX)
{
logError(jschX.getMessage());
}
strUserName = userName;
strPassword = password;
strConnectionIP = connectionIP;
}
public SSHManager(String userName, String password,
String connectionIP, String knownHostsFileName)
{
doCommonConstructorActions(userName, password,
connectionIP, knownHostsFileName);
intConnectionPort = 22;
intTimeOut = 60000;
}
public SSHManager(String userName, String password, String connectionIP,
String knownHostsFileName, int connectionPort)
{
doCommonConstructorActions(userName, password, connectionIP,
knownHostsFileName);
intConnectionPort = connectionPort;
intTimeOut = 60000;
}
public SSHManager(String userName, String password, String connectionIP,
String knownHostsFileName, int connectionPort, int timeOutMilliseconds)
{
doCommonConstructorActions(userName, password, connectionIP,
knownHostsFileName);
intConnectionPort = connectionPort;
intTimeOut = timeOutMilliseconds;
}
public String connect()
{
String errorMessage = null;
try
{
sesConnection = jschSSHChannel.getSession(strUserName,
strConnectionIP, intConnectionPort);
sesConnection.setPassword(strPassword);
// UNCOMMENT THIS FOR TESTING PURPOSES, BUT DO NOT USE IN PRODUCTION
// sesConnection.setConfig("StrictHostKeyChecking", "no");
sesConnection.connect(intTimeOut);
}
catch(JSchException jschX)
{
errorMessage = jschX.getMessage();
}
return errorMessage;
}
private String logError(String errorMessage)
{
if(errorMessage != null)
{
LOGGER.log(Level.SEVERE, "{0}:{1} - {2}",
new Object[]{strConnectionIP, intConnectionPort, errorMessage});
}
return errorMessage;
}
private String logWarning(String warnMessage)
{
if(warnMessage != null)
{
LOGGER.log(Level.WARNING, "{0}:{1} - {2}",
new Object[]{strConnectionIP, intConnectionPort, warnMessage});
}
return warnMessage;
}
public String sendCommand(String command)
{
StringBuilder outputBuffer = new StringBuilder();
try
{
Channel channel = sesConnection.openChannel("exec");
((ChannelExec)channel).setCommand(command);
InputStream commandOutput = channel.getInputStream();
channel.connect();
int readByte = commandOutput.read();
while(readByte != 0xffffffff)
{
outputBuffer.append((char)readByte);
readByte = commandOutput.read();
}
channel.disconnect();
}
catch(IOException ioX)
{
logWarning(ioX.getMessage());
return null;
}
catch(JSchException jschX)
{
logWarning(jschX.getMessage());
return null;
}
return outputBuffer.toString();
}
public void close()
{
sesConnection.disconnect();
}
}
用於測試。
/**
* Test of sendCommand method, of class SSHManager.
*/
@Test
public void testSendCommand()
{
System.out.println("sendCommand");
/**
* YOU MUST CHANGE THE FOLLOWING
* FILE_NAME: A FILE IN THE DIRECTORY
* USER: LOGIN USER NAME
* PASSWORD: PASSWORD FOR THAT USER
* HOST: IP ADDRESS OF THE SSH SERVER
**/
String command = "ls FILE_NAME";
String userName = "USER";
String password = "PASSWORD";
String connectionIP = "HOST";
SSHManager instance = new SSHManager(userName, password, connectionIP, "");
String errorMessage = instance.connect();
if(errorMessage != null)
{
System.out.println(errorMessage);
fail();
}
String expResult = "FILE_NAME\n";
// call sendCommand for each command and the output
//(without prompts) is returned
String result = instance.sendCommand(command);
// close only after all commands are sent
instance.close();
assertEquals(expResult, result);
}
gritty終端的編寫使用Jsch,但更好的處理和vt102仿真。你可以看看那裏的代碼。我們使用它,它工作得很好。
這是一個無恥的插件,但我現在只是writing一些廣泛的Javadoc for JSch。
另外,現在在JSch Wiki中有一個Manual(主要由我編寫)。
關於原始問題,沒有真正的處理流的例子。 一如既往地完成流的讀/寫。
但是,根本不可能有一個確定的方法來通過讀取shell的輸出(這與SSH協議無關)知道shell中的一條命令何時完成。
如果shell是交互式的,即它有一個終端連接,它通常會打印一個提示符,您可以嘗試識別。但至少理論上這個提示字符串也可能發生在命令的正常輸出中。如果您想確定,請爲每個命令打開單個exec
通道,而不是使用shell通道。我認爲,shell頻道主要用於人類用戶的交互式使用。
我掙扎了半天才得到JSCH的工作,而沒有使用System.in作爲輸入流無濟於事。我嘗試了Ganymed http://www.ganymed.ethz.ch/ssh2/,並在5分鐘內完成。所有的例子似乎都針對應用程序的一種用法,沒有任何例子顯示我需要什麼。 Ganymed的例子Basic.java Baaaboof擁有我需要的一切。
我從2000年開始就使用JSCH,但仍然覺得它是一個很好的庫。我同意它沒有被很好地記錄下來,但提供的示例看起來足夠好,足以在幾分鐘內理解這一點,而且用戶友好的Swing雖然是非常原始的方法,但可以快速測試示例以確保其實際工作。並不總是這樣,每個優秀的項目都需要比寫入代碼量多三倍的文檔,即使存在這樣的代碼,這並不總是有助於更快地編寫概念的工作原型。
如果你正在尋找JSCH..this的文檔可以幫助你:http://epaul.github.io/jsch-documentation/javadoc/com/jcraft/jsch/Session.html – 2016-03-23 04:45:04
用法:
String remoteCommandOutput = exec("ssh://user:[email protected]/work/dir/path", "ls -t | head -n1");
String remoteShellOutput = shell("ssh://user:[email protected]/work/dir/path", "ls");
shell("ssh://user:[email protected]/work/dir/path", "ls", System.out);
shell("ssh://user:[email protected]", System.in, System.out);
sftp("file:/C:/home/file.txt", "ssh://user:[email protected]/home");
sftp("ssh://user:[email protected]/home/file.txt", "file:/C:/home");
實現:
import static com.google.common.base.Preconditions.checkState;
import static java.lang.Thread.sleep;
import static org.apache.commons.io.FilenameUtils.getFullPath;
import static org.apache.commons.io.FilenameUtils.getName;
import static org.apache.commons.lang3.StringUtils.trim;
import com.google.common.collect.ImmutableMap;
import com.jcraft.jsch.Channel;
import com.jcraft.jsch.ChannelExec;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.ChannelShell;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.UIKeyboardInteractive;
import com.jcraft.jsch.UserInfo;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.PrintWriter;
import java.net.URI;
import java.util.Map;
import java.util.Properties;
public final class SshUtils {
private static final Logger LOG = LoggerFactory.getLogger(SshUtils.class);
private static final String SSH = "ssh";
private static final String FILE = "file";
private SshUtils() {
}
/**
* <pre>
* <code>
* sftp("file:/C:/home/file.txt", "ssh://user:[email protected]/home");
* sftp("ssh://user:[email protected]/home/file.txt", "file:/C:/home");
* </code>
*
* <pre>
*
* @param fromUri
* file
* @param toUri
* directory
*/
public static void sftp(String fromUri, String toUri) {
URI from = URI.create(fromUri);
URI to = URI.create(toUri);
if (SSH.equals(to.getScheme()) && FILE.equals(from.getScheme()))
upload(from, to);
else if (SSH.equals(from.getScheme()) && FILE.equals(to.getScheme()))
download(from, to);
else
throw new IllegalArgumentException();
}
private static void upload(URI from, URI to) {
try (SessionHolder<ChannelSftp> session = new SessionHolder<>("sftp", to);
FileInputStream fis = new FileInputStream(new File(from))) {
LOG.info("Uploading {} --> {}", from, session.getMaskedUri());
ChannelSftp channel = session.getChannel();
channel.connect();
channel.cd(to.getPath());
channel.put(fis, getName(from.getPath()));
} catch (Exception e) {
throw new RuntimeException("Cannot upload file", e);
}
}
private static void download(URI from, URI to) {
File out = new File(new File(to), getName(from.getPath()));
try (SessionHolder<ChannelSftp> session = new SessionHolder<>("sftp", from);
OutputStream os = new FileOutputStream(out);
BufferedOutputStream bos = new BufferedOutputStream(os)) {
LOG.info("Downloading {} --> {}", session.getMaskedUri(), to);
ChannelSftp channel = session.getChannel();
channel.connect();
channel.cd(getFullPath(from.getPath()));
channel.get(getName(from.getPath()), bos);
} catch (Exception e) {
throw new RuntimeException("Cannot download file", e);
}
}
/**
* <pre>
* <code>
* shell("ssh://user:[email protected]", System.in, System.out);
* </code>
* </pre>
*/
public static void shell(String connectUri, InputStream is, OutputStream os) {
try (SessionHolder<ChannelShell> session = new SessionHolder<>("shell", URI.create(connectUri))) {
shell(session, is, os);
}
}
/**
* <pre>
* <code>
* String remoteOutput = shell("ssh://user:[email protected]/work/dir/path", "ls")
* </code>
* </pre>
*/
public static String shell(String connectUri, String command) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
shell(connectUri, command, baos);
return baos.toString();
} catch (RuntimeException e) {
LOG.warn(baos.toString());
throw e;
}
}
/**
* <pre>
* <code>
* shell("ssh://user:[email protected]/work/dir/path", "ls", System.out)
* </code>
* </pre>
*/
public static void shell(String connectUri, String script, OutputStream out) {
try (SessionHolder<ChannelShell> session = new SessionHolder<>("shell", URI.create(connectUri));
PipedOutputStream pipe = new PipedOutputStream();
PipedInputStream in = new PipedInputStream(pipe);
PrintWriter pw = new PrintWriter(pipe)) {
if (session.getWorkDir() != null)
pw.println("cd " + session.getWorkDir());
pw.println(script);
pw.println("exit");
pw.flush();
shell(session, in, out);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
private static void shell(SessionHolder<ChannelShell> session, InputStream is, OutputStream os) {
try {
ChannelShell channel = session.getChannel();
channel.setInputStream(is, true);
channel.setOutputStream(os, true);
LOG.info("Starting shell for " + session.getMaskedUri());
session.execute();
session.assertExitStatus("Check shell output for error details.");
} catch (InterruptedException | JSchException e) {
throw new RuntimeException("Cannot execute script", e);
}
}
/**
* <pre>
* <code>
* System.out.println(exec("ssh://user:[email protected]/work/dir/path", "ls -t | head -n1"));
* </code>
*
* <pre>
*
* @param connectUri
* @param command
* @return
*/
public static String exec(String connectUri, String command) {
try (SessionHolder<ChannelExec> session = new SessionHolder<>("exec", URI.create(connectUri))) {
String scriptToExecute = session.getWorkDir() == null
? command
: "cd " + session.getWorkDir() + "\n" + command;
return exec(session, scriptToExecute);
}
}
private static String exec(SessionHolder<ChannelExec> session, String command) {
try (PipedOutputStream errPipe = new PipedOutputStream();
PipedInputStream errIs = new PipedInputStream(errPipe);
InputStream is = session.getChannel().getInputStream()) {
ChannelExec channel = session.getChannel();
channel.setInputStream(null);
channel.setErrStream(errPipe);
channel.setCommand(command);
LOG.info("Starting exec for " + session.getMaskedUri());
session.execute();
String output = IOUtils.toString(is);
session.assertExitStatus(IOUtils.toString(errIs));
return trim(output);
} catch (InterruptedException | JSchException | IOException e) {
throw new RuntimeException("Cannot execute command", e);
}
}
public static class SessionHolder<C extends Channel> implements Closeable {
private static final int DEFAULT_CONNECT_TIMEOUT = 5000;
private static final int DEFAULT_PORT = 22;
private static final int TERMINAL_HEIGHT = 1000;
private static final int TERMINAL_WIDTH = 1000;
private static final int TERMINAL_WIDTH_IN_PIXELS = 1000;
private static final int TERMINAL_HEIGHT_IN_PIXELS = 1000;
private static final int DEFAULT_WAIT_TIMEOUT = 100;
private String channelType;
private URI uri;
private Session session;
private C channel;
public SessionHolder(String channelType, URI uri) {
this(channelType, uri, ImmutableMap.of("StrictHostKeyChecking", "no"));
}
public SessionHolder(String channelType, URI uri, Map<String, String> props) {
this.channelType = channelType;
this.uri = uri;
this.session = newSession(props);
this.channel = newChannel(session);
}
private Session newSession(Map<String, String> props) {
try {
Properties config = new Properties();
config.putAll(props);
JSch jsch = new JSch();
Session newSession = jsch.getSession(getUser(), uri.getHost(), getPort());
newSession.setPassword(getPass());
newSession.setUserInfo(new User(getUser(), getPass()));
newSession.setDaemonThread(true);
newSession.setConfig(config);
newSession.connect(DEFAULT_CONNECT_TIMEOUT);
return newSession;
} catch (JSchException e) {
throw new RuntimeException("Cannot create session for " + getMaskedUri(), e);
}
}
@SuppressWarnings("unchecked")
private C newChannel(Session session) {
try {
Channel newChannel = session.openChannel(channelType);
if (newChannel instanceof ChannelShell) {
ChannelShell channelShell = (ChannelShell) newChannel;
channelShell.setPtyType("ANSI", TERMINAL_WIDTH, TERMINAL_HEIGHT, TERMINAL_WIDTH_IN_PIXELS, TERMINAL_HEIGHT_IN_PIXELS);
}
return (C) newChannel;
} catch (JSchException e) {
throw new RuntimeException("Cannot create " + channelType + " channel for " + getMaskedUri(), e);
}
}
public void assertExitStatus(String failMessage) {
checkState(channel.getExitStatus() == 0, "Exit status %s for %s\n%s", channel.getExitStatus(), getMaskedUri(), failMessage);
}
public void execute() throws JSchException, InterruptedException {
channel.connect();
channel.start();
while (!channel.isEOF())
sleep(DEFAULT_WAIT_TIMEOUT);
}
public Session getSession() {
return session;
}
public C getChannel() {
return channel;
}
@Override
public void close() {
if (channel != null)
channel.disconnect();
if (session != null)
session.disconnect();
}
public String getMaskedUri() {
return uri.toString().replaceFirst(":[^:]*[email protected]", "@");
}
public int getPort() {
return uri.getPort() < 0 ? DEFAULT_PORT : uri.getPort();
}
public String getUser() {
return uri.getUserInfo().split(":")[0];
}
public String getPass() {
return uri.getUserInfo().split(":")[1];
}
public String getWorkDir() {
return uri.getPath();
}
}
private static class User implements UserInfo, UIKeyboardInteractive {
private String user;
private String pass;
public User(String user, String pass) {
this.user = user;
this.pass = pass;
}
@Override
public String getPassword() {
return pass;
}
@Override
public boolean promptYesNo(String str) {
return false;
}
@Override
public String getPassphrase() {
return user;
}
@Override
public boolean promptPassphrase(String message) {
return true;
}
@Override
public boolean promptPassword(String message) {
return true;
}
@Override
public void showMessage(String message) {
// do nothing
}
@Override
public String[] promptKeyboardInteractive(String destination, String name, String instruction, String[] prompt, boolean[] echo) {
return null;
}
}
}
注意,慈善Leschinski的答案可能有一點問題的時候存在一些與響應延遲。例如:
的lparstat 1 5返回一個響應行和作品,
的lparstat 5 1應該回到5日線,但只返回第一個
我已經把命令的輸出,同時在另一個內部...我敢肯定有一個更好的辦法,我只好這樣做,因爲速戰速決
while (commandOutput.available() > 0) {
while (readByte != 0xffffffff) {
outputBuffer.append((char) readByte);
readByte = commandOutput.read();
}
try {Thread.sleep(1000);} catch (Exception ee) {}
}
- 1. Jsch - 通過ssh發送命令
- 2. 如何使用Jsch通過SSH運行需要密碼的命令
- 3. 運行sudo命令通過ssh
- 4. 通過ssh運行多個命令
- 5. 通過ssh遠程運行命令
- 6. 通過ssh運行mysql命令
- 7. 通過ssh運行Ansible單一命令
- 8. 當我使用JSch運行命令
- 9. 無法在ssh來執行命令Jsch
- 10. jsch並運行「命令蘇 - 」
- 11. 通過Jsch Shell執行多個命令
- 12. 實現JSch在Android應用程序中運行ssh命令
- 13. 使用JSch執行命令
- 14. 使用Maven3通過ssh運行遠程命令
- 15. 通過SSH運行SSH屏幕上的命令phpseclib
- 16. 如何通過Java中的JSch SSH會話使用需要交互式終端的命令行* nix命令?
- 17. 通過SSH運行遠程命令,但在後臺運行
- 18. 通過ssh運行命令也運行.bashrc?
- 19. 通過ssh通過ssh通過Java與JSch發送命令到遠程服務器
- 20. 使用ssh運行遠程awk命令
- 21. 用JSch執行source命令
- 22. 通過Wi-Fi從Android應用程序運行SSH命令
- 23. 如何通過PHP執行SSH命令
- 24. 如何通過robotframework執行ssh命令
- 25. 通過SSH執行大量命令
- 26. 無法通過ssh執行mpiexec命令
- 27. 通過ssh執行命令,然後運行bash
- 28. 使用PHP執行shell命令通過ssh使用PHP
- 29. 帶x11forwarding的Jsch shell命令 - 重用通道並運行新命令
- 30. 使用參數通過ssh使用python腳本的命令運行腳本
'InputStream commandOutput'似乎沒有明確關閉。它會造成任何泄漏? – stanleyxu2005 2014-03-19 06:38:18
@ stanleyxu2005 http://docs.oracle.com/javase/7/docs/api/java/io/InputStream.html#close%28%29 InputStream的close方法什麼也不做。 – 2014-04-22 01:30:58
我希望我可以給你更多的答案 – solti 2014-04-28 17:53:08