Java JSch遠(yuǎn)程執(zhí)行Shell命令的方法
背景
項(xiàng)目需求,需要遠(yuǎn)程 ssh 登錄到某個(gè)節(jié)點(diǎn)執(zhí)行 shell 命令來(lái)完成任務(wù)。對(duì)于這種需求,如果不用 java 程序,直接 linux 的 ssh 命令就可以完成,但是在編碼到程序中時(shí)需要相關(guān)的程序包來(lái)完成,本文主要介紹在 java 中如何使用 JSch 包實(shí)現(xiàn) ssh 遠(yuǎn)程連接并執(zhí)行命令。
JSch 簡(jiǎn)介
JSch 是Java Secure Channel的縮寫(xiě)。JSch是一個(gè)SSH2的純Java實(shí)現(xiàn)。它允許你連接到一個(gè)SSH服務(wù)器,并且可以使用端口轉(zhuǎn)發(fā),X11轉(zhuǎn)發(fā),文件傳輸?shù)?,?dāng)然你也可以集成它的功能到你自己的應(yīng)用程序??蚣躩sch很老的框架,更新到2016年,現(xiàn)在也不更新了。
JSch 使用 shell 執(zhí)行命令,有兩種方法
- ChannelExec: 一次執(zhí)行一條命令,一般我們用這個(gè)就夠了。
- ChannelShell: 可執(zhí)行多條命令,平時(shí)開(kāi)發(fā)用的不多,根據(jù)需要來(lái)吧;
ChannelExec channelExec = (ChannelExec) session.openChannel("exec");//只能執(zhí)行一條指令(也可執(zhí)行符合指令) ChannelShell channelShell = (ChannelShell) session.openChannel("shell");//可執(zhí)行多條指令 不過(guò)需要輸入輸出流
1. ChannelExec
每個(gè)命令之間用 ; 隔開(kāi)。說(shuō)明:各命令的執(zhí)行給果,不會(huì)影響其它命令的執(zhí)行。換句話說(shuō),各個(gè)命令都會(huì)執(zhí)行,但不保證每個(gè)命令都執(zhí)行成功。
每個(gè)命令之間用 && 隔開(kāi)。說(shuō)明:若前面的命令執(zhí)行成功,才會(huì)去執(zhí)行后面的命令。這樣可以保證所有的命令執(zhí)行完畢后,執(zhí)行過(guò)程都是成功的。
每個(gè)命令之間用 || 隔開(kāi)。說(shuō)明:|| 是或的意思,只有前面的命令執(zhí)行失敗后才去執(zhí)行下一條命令,直到執(zhí)行成功一條命令為止。
2. ChannelShell
對(duì)于ChannelShell,以輸入流的形式,可執(zhí)行多條指令,這就像在本地計(jì)算機(jī)上使用交互式shell(它通常用于:交互式使用)。如要要想停止,有兩種方式:
發(fā)送一個(gè)exit命令,告訴程序本次交互結(jié)束;
使用字節(jié)流中的available方法,來(lái)獲取數(shù)據(jù)的總大小,然后循環(huán)去讀。
使用示例
1. 引入 pom 依賴(lài)
<dependency> ? ?<groupId>com.jcraft</groupId> ? ?<artifactId>jsch</artifactId> ? ?<version>0.1.53</version> </dependency>
2. jsch 使用示例
在此封裝了一個(gè) Shell 工具類(lèi),用來(lái)執(zhí)行 shell 命令,具體使用細(xì)節(jié)在代碼注釋中有說(shuō)明,可以直接拷貝并使用,代碼如下:
package org.example.shell; /** ?* Created by qianghaohao on 2021/3/28 ?*/ import com.jcraft.jsch.ChannelExec; import com.jcraft.jsch.JSch; import com.jcraft.jsch.Session; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; /** ?* @description: ?* @author: qianghaohao ?* @time: 2021/3/28 ?*/ public class Shell { ? ? private String host; ? ? private String username; ? ? private String password; ? ? private int port = 22; ? ? private int timeout = 60 * 60 * 1000; ? ? public Shell(String host, String username, String password, int port, int timeout) { ? ? ? ? this.host = host; ? ? ? ? this.username = username; ? ? ? ? this.password = password; ? ? ? ? this.port = port; ? ? ? ? this.timeout = timeout; ? ? } ? ? public Shell(String host, String username, String password) { ? ? ? ? this.host = host; ? ? ? ? this.username = username; ? ? ? ? this.password = password; ? ? } ? ? public String execCommand(String cmd) { ? ? ? ? JSch jSch = new JSch(); ? ? ? ? Session session = null; ? ? ? ? ChannelExec channelExec = null; ? ? ? ? BufferedReader inputStreamReader = null; ? ? ? ? BufferedReader errInputStreamReader = null; ? ? ? ? StringBuilder runLog = new StringBuilder(""); ? ? ? ? StringBuilder errLog = new StringBuilder(""); ? ? ? ? try { ? ? ? ? ? ? // 1. 獲取 ssh session ? ? ? ? ? ? session = jSch.getSession(username, host, port); ? ? ? ? ? ? session.setPassword(password); ? ? ? ? ? ? session.setTimeout(timeout); ? ? ? ? ? ? session.setConfig("StrictHostKeyChecking", "no"); ? ? ? ? ? ? session.connect(); ?// 獲取到 ssh session ? ? ? ? ? ? // 2. 通過(guò) exec 方式執(zhí)行 shell 命令 ? ? ? ? ? ? channelExec = (ChannelExec) session.openChannel("exec"); ? ? ? ? ? ? channelExec.setCommand(cmd); ? ? ? ? ? ? channelExec.connect(); ?// 執(zhí)行命令 ? ? ? ? ? ? // 3. 獲取標(biāo)準(zhǔn)輸入流 ? ? ? ? ? ? inputStreamReader = new BufferedReader(new InputStreamReader(channelExec.getInputStream())); ? ? ? ? ? ? // 4. 獲取標(biāo)準(zhǔn)錯(cuò)誤輸入流 ? ? ? ? ? ? errInputStreamReader = new BufferedReader(new InputStreamReader(channelExec.getErrStream())); ? ? ? ? ? ? // 5. 記錄命令執(zhí)行 log ? ? ? ? ? ? String line = null; ? ? ? ? ? ? while ((line = inputStreamReader.readLine()) != null) { ? ? ? ? ? ? ? ? runLog.append(line).append("\n"); ? ? ? ? ? ? } ? ? ? ? ? ? // 6. 記錄命令執(zhí)行錯(cuò)誤 log ? ? ? ? ? ? String errLine = null; ? ? ? ? ? ? while ((errLine = errInputStreamReader.readLine()) != null) { ? ? ? ? ? ? ? ? errLog.append(errLine).append("\n"); ? ? ? ? ? ? } ? ? ? ? ? ? // 7. 輸出 shell 命令執(zhí)行日志 ? ? ? ? ? ? System.out.println("exitStatus=" + channelExec.getExitStatus() + ", openChannel.isClosed=" ? ? ? ? ? ? ? ? ? ? + channelExec.isClosed()); ? ? ? ? ? ? System.out.println("命令執(zhí)行完成,執(zhí)行日志如下:"); ? ? ? ? ? ? System.out.println(runLog.toString()); ? ? ? ? ? ? System.out.println("命令執(zhí)行完成,執(zhí)行錯(cuò)誤日志如下:"); ? ? ? ? ? ? System.out.println(errLog.toString()); ? ? ? ? } catch (Exception e) { ? ? ? ? ? ? e.printStackTrace(); ? ? ? ? } finally { ? ? ? ? ? ? try { ? ? ? ? ? ? ? ? if (inputStreamReader != null) { ? ? ? ? ? ? ? ? ? ? inputStreamReader.close(); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? if (errInputStreamReader != null) { ? ? ? ? ? ? ? ? ? ? errInputStreamReader.close(); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? if (channelExec != null) { ? ? ? ? ? ? ? ? ? ? channelExec.disconnect(); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? if (session != null) { ? ? ? ? ? ? ? ? ? ? session.disconnect(); ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } catch (IOException e) { ? ? ? ? ? ? ? ? e.printStackTrace(); ? ? ? ? ? ? } ? ? ? ? } ? ? ? ? return runLog.toString(); ? ? } }
上述工具類(lèi)使用:
package org.example; import org.example.shell.Shell; /** ?* Hello world! ?* ?*/ public class App { ? ? public static void main( String[] args ) { ? ? ? ? String cmd = "ls -1"; ? ? ? ? Shell shell = new Shell("192.168.10.10", "ubuntu", "11111"); ? ? ? ? String execLog = shell.execCommand(cmd); ? ? ? ? System.out.println(execLog); ? ? } }
需要注意的點(diǎn)
如果需要后臺(tái)執(zhí)行某個(gè)命令,不能直接 <命令> + & 的方式執(zhí)行,這樣在 JSch 中不生效,需要寫(xiě)成這樣的格式:<命令> > /dev/null 2>&1 &。比如要后臺(tái)執(zhí)行 sleep 60,需要寫(xiě)成 sleep 60 > /dev/null 2>&1
具體 issue 見(jiàn)這里:https://stackoverflow.com/questions/37833683/running-programs-using-jsch-in-the-background
參考文檔
https://www.cnblogs.com/slankka/p/11988477.html
https://blog.csdn.net/sinat_24928447/article/details/83022818
到此這篇關(guān)于Java JSch遠(yuǎn)程執(zhí)行Shell命令的方法的文章就介紹到這了,更多相關(guān)Java JSch遠(yuǎn)程執(zhí)行Shell命令內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- java中如何執(zhí)行xshell命令
- java在linux本地執(zhí)行shell命令的實(shí)現(xiàn)方法
- Java中如何執(zhí)行多條shell/bat命令
- Java程序去調(diào)用并執(zhí)行shell腳本及問(wèn)題總結(jié)(推薦)
- 基于Java實(shí)現(xiàn)ssh命令登錄主機(jī)執(zhí)行shell命令過(guò)程解析
- Java代碼執(zhí)行shell命令的實(shí)現(xiàn)
- java調(diào)用shell命令并獲取執(zhí)行結(jié)果的示例
- Shell執(zhí)行/調(diào)用Java/Jar程序例子的實(shí)例詳解
- java通過(guò)ssh連接服務(wù)器執(zhí)行shell命令詳解及實(shí)例
- Java執(zhí)行shell命令的實(shí)現(xiàn)
相關(guān)文章
SpringBoot使用AOP實(shí)現(xiàn)防重復(fù)提交功能
這篇文章主要為大家詳細(xì)介紹了SpringBoot如何使用AOP實(shí)現(xiàn)防重復(fù)提交功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-03-03java單點(diǎn)登錄(SSO)的實(shí)現(xiàn)
SSO是指在多個(gè)應(yīng)用系統(tǒng)中個(gè),用戶只需要登陸一次就可以訪問(wèn)所有相互信任的應(yīng)用系統(tǒng),本文主要介紹了java單點(diǎn)登錄的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2021-07-07Java實(shí)現(xiàn)導(dǎo)出Excel功能
通過(guò)java中Controller層,來(lái)接受請(qǐng)求,數(shù)據(jù)庫(kù)查詢到的數(shù)據(jù)進(jìn)行封裝,然后使用ExcelUtils進(jìn)行輸出,接下來(lái)通過(guò)本文給大家分享Java實(shí)現(xiàn)導(dǎo)出Excel功能的實(shí)例代碼,感興趣的朋友跟隨小編一起看看吧2021-11-11關(guān)于JWT與cookie和token的區(qū)別說(shuō)明
這篇文章主要介紹了JWT與cookie和token的區(qū)別說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10Java編程通過(guò)list接口實(shí)現(xiàn)數(shù)據(jù)的增刪改查代碼示例
這篇文章是介紹Java編程基礎(chǔ)方面的內(nèi)容,涉及l(fā)ist接口的操作,通過(guò)list接口實(shí)現(xiàn)對(duì)數(shù)據(jù)的增刪改查的相關(guān)代碼,具有一定參考價(jià)值,需要的朋友可以了解下。2017-10-10spring boot中多線程開(kāi)發(fā)的注意事項(xiàng)總結(jié)
spring boot 通過(guò)任務(wù)執(zhí)行器 taskexecutor 來(lái)實(shí)現(xiàn)多線程和并發(fā)編程。下面這篇文章主要給大家介紹了關(guān)于spring boot中多線程開(kāi)發(fā)的注意事項(xiàng),文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2018-09-09Java上傳文件進(jìn)度條的實(shí)現(xiàn)方法(附demo源碼下載)
這篇文章主要介紹了Java上傳文件進(jìn)度條的實(shí)現(xiàn)方法,可簡(jiǎn)單實(shí)現(xiàn)顯示文件上傳比特?cái)?shù)及進(jìn)度的功能,并附帶demo源碼供讀者下載參考,需要的朋友可以參考下2015-12-12Java中SimpleDateFormat 格式化日期的使用
本文主要介紹了Java中SimpleDateFormat 格式化日期的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03