2010-03-09 40 views
48

Tôi đang cố gắng chạy lệnh qua SSH với JSch, nhưng JSch hầu như không có tài liệu và các ví dụ tôi thấy là khủng khiếp. Ví dụ: this one không hiển thị mã để xử lý luồng đầu ra. Và, this one sử dụng hack xấu xí để biết thời điểm dừng đọc từ luồng đầu ra.Chạy lệnh qua SSH với JSch

Trả lời

74

Ví dụ mã sau được viết bằng Java sẽ cho phép bạn thực hiện bất kỳ lệnh nào trên máy tính nước ngoài thông qua SSH từ bên trong chương trình java. Bạn sẽ cần phải bao gồm tệp jar com.jcraft.jsch.

/* 
    * 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(); 
    } 

    } 

Để thử nghiệm.

/** 
    * 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); 
    } 
+0

'Lệnh InputStreamOutput' dường như không được đóng một cách rõ ràng. Nó sẽ tạo ra bất kỳ rò rỉ? – stanleyxu2005

+0

@ stanleyxu2005 http://docs.oracle.com/javase/7/docs/api/java/io/InputStream.html#close%28%29 Phương thức đóng của InputStream không làm gì cả. –

+2

Tôi ước tôi có thể cung cấp nhiều điểm hơn cho câu trả lời của bạn – solti

2

Thiết bị đầu cuối gritty được viết để sử dụng Jsch, nhưng xử lý tốt hơn và mô phỏng vt102. Bạn có thể xem mã ở đó. Chúng tôi sử dụng nó và nó hoạt động tốt.

5

sử dụng ssh từ java không nên khó như jsch làm cho nó. bạn có thể tốt hơn với sshj.

+0

cảm ơn, tôi sẽ thử điều đó khi tôi có cơ hội – jshen

27

Đây là một plug không biết xấu hổ, nhưng tôi bây giờ chỉ là writing một số mở rộng Javadoc for JSch.

Ngoài ra, hiện tại có Manual trong Wiki JSch (được viết chủ yếu bởi tôi).


Về câu hỏi ban đầu, không thực sự là ví dụ để xử lý luồng. Đọc/ghi luồng được thực hiện như mọi khi. Nhưng không có cách nào chắc chắn để biết khi nào một lệnh trong một trình bao đã kết thúc chỉ từ việc đọc đầu ra của trình bao (đây là độc lập với giao thức SSH).

Nếu trình bao tương tác, tức là trình bao có thiết bị đầu cuối được đính kèm, nó thường sẽ in lời nhắc mà bạn có thể cố gắng nhận ra. Nhưng ít nhất về mặt lý thuyết, chuỗi nhắc này cũng có thể xuất hiện ở đầu ra bình thường từ một lệnh. Nếu bạn muốn chắc chắn, hãy mở từng kênh exec cho mỗi lệnh thay vì sử dụng kênh vỏ. Các kênh vỏ được sử dụng chủ yếu để sử dụng tương tác bởi một người sử dụng con người, tôi nghĩ.

8

Tôi đã vật lộn trong nửa ngày để JSCH hoạt động mà không sử dụng System.in làm luồng đầu vào không có kết quả. Tôi đã thử Ganymed http://www.ganymed.ethz.ch/ssh2/ và nó đã diễn ra trong 5 phút. Tất cả các ví dụ dường như nhằm mục đích sử dụng một ứng dụng và không có ví dụ nào cho thấy những gì tôi cần. Ví dụ của Ganymed Basic.java Baaaboof Có mọi thứ tôi cần.

2

Tôi đang sử dụng JSCH từ khoảng năm 2000 và vẫn thấy nó là một thư viện tốt để sử dụng. Tôi đồng ý nó không được viết đủ tốt nhưng các ví dụ được cung cấp có vẻ đủ tốt để hiểu rằng nó được yêu cầu trong vài phút, và thân thiện với người dùng Swing, trong khi đây là cách tiếp cận khá độc đáo, cho phép kiểm tra ví dụ một cách nhanh chóng để đảm bảo nó thực sự hoạt động. Nó không phải luôn luôn đúng là mọi dự án tốt cần tài liệu gấp ba lần so với số lượng mã được viết, và ngay cả khi có như vậy, điều này không phải lúc nào cũng giúp viết nhanh hơn nguyên mẫu làm việc của khái niệm của bạn.

+0

Nếu bạn đang tìm kiếm tài liệu về JSCH..this có thể giúp bạn: http://epaul.github.io/jsch-documentation/javadoc/com/jcraft/jsch/Session.html –

7

Cách sử dụng:

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"); 

Thực hiện:

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; 
     } 
    } 
} 
1

Lưu ý rằng câu trả lời từ thiện Leschinski của có thể có một chút của một vấn đề khi có một số chậm trễ trong phản ứng. ví dụ:
lparstat 1 5 lợi nhuận một dòng phản ứng và các công trình,
lparstat 5 1 nên trả lại 5 dòng, nhưng chỉ trả lại là người đầu tiên

Tôi đã đưa ra lệnh trong khi bên trong một ... Tôi chắc chắn có một cách tốt hơn, tôi phải thực hiện việc này nhanh chóng sửa chữa

 while (commandOutput.available() > 0) { 
      while (readByte != 0xffffffff) { 
       outputBuffer.append((char) readByte); 
       readByte = commandOutput.read(); 
      } 
      try {Thread.sleep(1000);} catch (Exception ee) {} 
     } 
Các vấn đề liên quan