首发CSDN http://blog.csdn.net/basecn/archive/2010/12/16/6080741.aspx
作者:BaseCN Email:basecn@163.com
-----------------------------------------------
Jsch是JAVA的SSH客户端,使用的目的是执行远程服务器上的命令。
关于Session的使用,创建连接后这个session是一直可用的,所以不需要关闭。由Session中open的Channel在使用后应该关闭。
测试了exec的方式,具体参考jsch自带example中的Exec.java。
有两个问题:
1、无法执行多条命令,每次ChannelExec在connect前要先setCommand,最后一次的设置生效。
2、由于第一个原因的限制,如果执行的命令需要环境变量(如JAVA_HOME),就没有办法了。这种方式执行基本的ls,ps之类的命令没有问题,需要复杂的环境变量时有点力不从心。
要是哪位知道exec如何解决上面现两个问题,请分享一下!
-----------------------------------------------
虽然exec可以得到命令的执行结果,但无法满足应用,无奈之下放弃exec转而使用ChannelShell。
在使用ChannelShell的时候需要特别关注jsch的输入和输出流的设置。
输出
为了得到脚本的运行结果,设置jsch的outputStream为FileOutputStream,把shell的输出结果保存到本地文件。虽然最简单的方式是设置System.out为jsch的OutputStream,在控制台看到结果,只是无法保存下来。
- FileOutputStream fileOut = new FileOutputStream( outputFileName );
- channelShell.setOutputStream( fileOut );
输入
短时间运行的程序,输入可以直接设置为System.in,而长期运行的程序不能使用人工方式输入,必须由程序自动生成命令来执行。所以使用PipeStream来实现字符串输入命令:
- PipedInputStream pipeIn = new PipedInputStream();
- PipedOutputStream pipeOut = new PipedOutputStream( pipeIn );
- channelShell.setInputStream( pipeIn );
调用pipeOut.write( cmd.getBytes() );把生成的命令输出给ssh。
运行
jsch是以多线程方式运行的,所以代码在connect后如果不disconnect Channel和Session,以及相关的Stream,程序会一直等待,直到关闭,目前还没有找到判断关闭或主动关闭的方法,相信应该有这方面的机制。
要注意一个问题,相关的Stream和Channel是一定要关闭的,那么应该在什么时候来关。执行connect后,jsch接收客户端结果需要一定的时间(以秒计),如果马上关闭session就会发现什么都没接收到或内容不全。
可以采取两个办法来解决这个问题,一个开源一个节流
1、在connect增加一个等待延迟,等待1~2秒,这个是开源;
2、减小server端脚本的执行时间,这个是节流。给命令加上"nohup XXXX > output &",以后台方式运行,并把运行结果输出到服务器端的本地目录下。这样脚本的执行时间可以是最小。
-----------------------------------------------
最后还有一点注意,使用shell时,看到有的朋友说执行后没有结果。解决的办法是在命令后加上"/n"字符,server端就认为是一条完整命令了。很奇怪的特性!
-----------------------------------------------
附上类代码
- package jsch;
- import static java.lang.String.format;
- import java.io.Closeable;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.PipedInputStream;
- import java.io.PipedOutputStream;
- import com.jcraft.jsch.ChannelExec;
- import com.jcraft.jsch.ChannelShell;
- import com.jcraft.jsch.JSch;
- import com.jcraft.jsch.Session;
- import com.jcraft.jsch.UserInfo;
- import com.nsm.hermes.wand.Wand;
- public class SshExecuter
- implements Closeable{
- static long interval = 1000L;
- static int timeout = 3000;
- private SshInfo sshInfo = null;
- private JSch jsch = null;
- private Session session = null;
- private SshExecuter( SshInfo info ) throws Exception{
- sshInfo = info;
- jsch = new JSch();
- jsch.addIdentity( sshInfo.getKey() );
- session = jsch.getSession( sshInfo.getUser(),
- sshInfo.getHost(),
- sshInfo.getPort() );
- UserInfo ui = new SshUserInfo( sshInfo.getPassPhrase() );
- session.setUserInfo( ui );
- session.connect();
- }
- public long shell( String cmd, String outputFileName )
- throws Exception{
- long start = System.currentTimeMillis();
- ChannelShell channelShell = (ChannelShell)session.openChannel( "shell" );
- PipedInputStream pipeIn = new PipedInputStream();
- PipedOutputStream pipeOut = new PipedOutputStream( pipeIn );
- FileOutputStream fileOut = new FileOutputStream( outputFileName );
- channelShell.setInputStream( pipeIn );
- channelShell.setOutputStream( fileOut );
- channelShell.connect( timeout );
- pipeOut.write( cmd.getBytes() );
- Thread.sleep( interval );
- pipeOut.close();
- pipeIn.close();
- fileOut.close();
- channelShell.disconnect();
- return System.currentTimeMillis() - start;
- }
- public int exec( String cmd )
- throws Exception{
- ChannelExec channelExec = (ChannelExec)session.openChannel( "exec" );
- channelExec.setCommand( cmd );
- channelExec.setInputStream( null );
- channelExec.setErrStream( System.err );
- InputStream in = channelExec.getInputStream();
- channelExec.connect();
- int res = -1;
- StringBuffer buf = new StringBuffer( 1024 );
- byte[] tmp = new byte[ 1024 ];
- while ( true ) {
- while ( in.available() > 0 ) {
- int i = in.read( tmp, 0, 1024 );
- if ( i < 0 ) break;
- buf.append( new String( tmp, 0, i ) );
- }
- if ( channelExec.isClosed() ) {
- res = channelExec.getExitStatus();
- System.out.println( format( "Exit-status: %d", res ) );
- break;
- }
- Wand.waitA( 100 );
- }
- System.out.println( buf.toString() );
- channelExec.disconnect();
- return res;
- }
- public static SshExecuter newInstance()
- throws Exception{
- String host = "localhost";
- Integer port = 22;
- String user = "hadoop";
- String key = "./id_dsa";
- String passPhrase = "";
- SshInfo i = new SshInfo( host, port, user, key, passPhrase );
- return new SshExecuter( i );
- }
- public Session getSession(){
- return session;
- }
- public void close()
- throws IOException{
- getSession().disconnect();
- }
- }
- class SshInfo{
- String host = null;
- Integer port = 22;
- String user = null;
- String key = null;
- String passPhrase = null;
- public SshInfo( String host,
- Integer port,
- String user,
- String key,
- String passPhrase ){
- super();
- this.host = host;
- this.port = port;
- this.user = user;
- this.key = key;
- this.passPhrase = passPhrase;
- }
- public String getHost(){
- return host;
- }
- public Integer getPort(){
- return port;
- }
- public String getUser(){
- return user;
- }
- public String getKey(){
- return key;
- }
- public String getPassPhrase(){
- return passPhrase;
- }
- }
- class SshUserInfo implements UserInfo{
- private String passphrase = null;
- public SshUserInfo( String passphrase ){
- super();
- this.passphrase = passphrase;
- }
- public String getPassphrase(){
- return passphrase;
- }
- public String getPassword(){
- return null;
- }
- public boolean promptPassphrase( String pass ){
- return true;
- }
- public boolean promptPassword( String pass ){
- return true;
- }
- public boolean promptYesNo( String arg0 ){
- return true;
- }
- public void showMessage( String m ){
- System.out.println( m );
- }
- }