天天看點

jsch中ChannelShell與ChannelExec差別環境分析

環境

jdk:java7

作業系統: win7

因為架構

jsch

比較老,是以對

java

版本要求好低。

分析

架構

jsch

我就不說了,很老的架構,現在也不更新了。

官網:http://www.jcraft.com/jsch/

最近在用

jsch

中的

ChannelShell

時,遇到問題:

①這個方法會傳回指令提示符,很煩;

比如我要執行下面幾個指令:

ChannelShell channel = (ChannelShell) session.openChannel("shell");
        channel.connect();
        InputStream inputStream = channel.getInputStream();
        OutputStream outputStream = channel.getOutputStream();
        String cmd = "ls \n\r";
        outputStream.write(cmd.getBytes());
        String cmd2 = "cd /home/jenkins/workspace/ggservice \n\r";
        outputStream.write(cmd2.getBytes());
        String cmd3 = "pwd \n\r";
        outputStream.write(cmd3.getBytes());
        outputStream.flush();
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream));

        String msg = null;
        while((msg = in.readLine())!=null){
            System.out.println(msg);
        }
        in.close();
           

得到的結果是:

Last login: Fri Apr  ::  from .

ls 

cd /home/jenkins/workspace/ggservice 

pwd 

[[email protected] ~]# ls 
bintray-sbt-rpm.repo tables_mysql_innodb.sql
[[email protected] ~]# 
[[email protected] ~]# cd /home/jenkins/workspace/ggservice 
[[email protected] ggservice]# 
[[email protected] ggservice]# pwd 
/home/jenkins/workspace/ggservice
[[email protected] ggservice]# 
           

可以看出,連

[[email protected] ~]

這樣的指令提示符和輸入的指令都出來,我其實是不需要這個,我要的隻是結果。

②由于使用

BufferedReader

readLine()

方法,結果會産生阻塞。

BufferedReader in = new BufferedReader(new InputStreamReader(inputStream)); String msg = null;
while((msg = in.readLine())!=null){//當所有的指令都執行完畢後,就會産生阻塞
    System.out.println(msg);
}
in.close();
           

如下圖:

jsch中ChannelShell與ChannelExec差別環境分析

為什麼會這樣呢?

因為我們建立的是

shell

管道,并且我們又使用

readLine

方法,當指令全部執行完畢後,遠端端并不知道執行完畢,還在等待接受資料,是以呢

reandLine

就一直阻塞在那裡。

即便你換成

read

方法還是一樣的,因為

shell

管道本身就是互動模式的。要想停止,有兩種方式:

①人為的發送一個

exit

指令,告訴程式本次互動結束啦

②使用位元組流中的

available

方法,來擷取資料的總大小,然後循環去讀。

①代碼:

InputStream inputStream = channel.getInputStream();//從遠端端到達的所有資料都能從這個流中讀取到
        OutputStream outputStream = channel.getOutputStream();//寫入該流的所有資料都将發送到遠端端。
        //使用PrintWriter流的目的就是為了使用println這個方法
        //好處就是不需要每次手動給字元串加\n
        PrintWriter printWriter = new PrintWriter(outputStream);

        String cmd = "ls";
        printWriter.println(cmd);
        String cmd2 = "cd /home/jenkins/workspace/ggservice";
        printWriter.println(cmd2);
        String cmd3 = "ls";
        printWriter.println(cmd3);
        printWriter.println("exit");//加上個就是為了,結束本次互動
        printWriter.flush();
BufferedReader in = new BufferedReader(new InputStreamReader(inputStream));
String msg = null;
        while((msg = in.readLine())!=null){
            System.out.println(msg);
        }
        in.close();
           

②代碼:

...代碼省略 ...

ChannelShell channel = (ChannelShell) session.openChannel("shell");
channel.connect();
//從遠端端到達的所有資料都能從這個流中讀取到
InputStream in = channel.getInputStream();
//寫入該流的所有資料都将發送到遠端端。
OutputStream outputStream = channel.getOutputStream();
byte[] tmp=new byte[];
while(true){
  while(in.available()>){
    int i=in.read(tmp, , );
    if(i<)break;
    System.out.print(new String(tmp, , i));
  }
  if(channel.isClosed()){
    if(in.available()>) continue;
    System.out.println("exit-status: "+channel.getExitStatus());
    break;
  }
}
           

這樣就不會阻塞啦

最後我就去查ChannelShell和ChannelExec差別

ChannelShell

對于ChannelShell,以輸入流的形式,提供指令和輸入這些指令,這就像在本地計算機上使用互動式shell

(它通常用于:互動式使用)

ChannelExec

對于ChannelExec,在調用connect()方法之前這個指令提供了setCommand()方法,

并且這些指令作為輸入将以輸入流的形式被發送出去。

(通常,你隻能有調用setCommand()方法一次,多次調用隻有最後一次生效),

但是你可以使用普通shell的分隔符(&,&&,|,||,; , \n, 複合指令)來提供多個指令。

這就像在你本機上執行一個shell腳本一樣(當然,如果一個指令本身就是個互動式shell,這樣就像ChannelShell)

明顯:使用指令通道更容易,因為您不需要處理指令提示符。

參考連結

http://stackoverflow.com/a/6771417/6952713

http://stackoverflow.com/a/6266308/6952713