Java執(zhí)行shell命令的實現(xiàn)
前言
java執(zhí)行shell命令的方式有很多種,但是在應(yīng)用的過程中,我們可能會遇上一些特殊的情況,導(dǎo)致執(zhí)行腳本失敗,不生效的場景。
一、案例
場景
java服務(wù),如果需要服務(wù)自動重啟。那么我們通過java執(zhí)行shell命令,使用常用jdk的方法:Runtime.getRuntime().exec(command)的方式,重啟服務(wù),可能會導(dǎo)致重啟失敗。
原因
- java執(zhí)行本地命令啟動的是一個子進(jìn)程處理,默認(rèn)情況下子進(jìn)程與父進(jìn)程I/O通過管道相連(ProcessBuilder.Redirect.PIPE)
- 當(dāng)服務(wù)執(zhí)行自身重啟命令時,父進(jìn)程關(guān)閉導(dǎo)致管道連接中斷,將導(dǎo)致子進(jìn)程也崩潰,而無法完成后續(xù)啟動
解決方案
- 設(shè)置子進(jìn)程的I/O源或目標(biāo)將與當(dāng)前進(jìn)程的相同,兩者相互獨立
- 設(shè)置子進(jìn)程IO輸出重定向到指定文件
這里我們采用第一種解決方案
ProcessBuilder pb = new ProcessBuilder("service","java-service","restart"); pb.redirectOutput(ProcessBuilder.Redirect.INHERIT); pb.redirectError(ProcessBuilder.Redirect.INHERIT); pb.redirectInput(ProcessBuilder.Redirect.INHERIT); pb.start();
ProcessBuilder 也是J2SE1.5 就有了的類。此類用于創(chuàng)建操作系統(tǒng)進(jìn)程,它提供一種啟動和管理進(jìn)程(也就是應(yīng)用程序)的方法。在J2SE 1.5之前,都是由Process類處來實現(xiàn)進(jìn)程的控制管理。
static ProcessBuilder.Redirect DISCARD 表示將丟棄子進(jìn)程輸出。 static ProcessBuilder.Redirect INHERIT 表示子進(jìn)程I / O源或目標(biāo)將與當(dāng)前進(jìn)程的相同。 static ProcessBuilder.Redirect PIPE 表示子進(jìn)程I / O將通過管道連接到當(dāng)前Java進(jìn)程。
ProcessBuilder 可配置執(zhí)行腳本的子進(jìn)程I / O源或目標(biāo)將與當(dāng)前進(jìn)程的相同。綁定之后,執(zhí)行重啟,就能成功,管道不會斷開。
ProcessBuilder 可參考連接:
ProcessBuilder api
ProcessBuilder 中文文檔
二、拓展
創(chuàng)建臨時腳本,執(zhí)行shell命令
<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.11.0</version> </dependency>
可直接復(fù)制使用,注意引入maven包
public static String runAndResult(String cmd){ StringBuilder sb = new StringBuilder(); BufferedReader br = null; boolean execFlag = true; String uuid = UUID.randomUUID().toString().replace("-",""); String tempFileName = "./temp" + uuid +".sh"; try { String osName = System.getProperty("os.name").toUpperCase(Locale.ENGLISH); if (osName.matches("^(?i)LINUX.*$") || osName.contains("MAC")) { FileWriter execute_fw = new FileWriter(tempFileName); BufferedWriter execute_bw=new BufferedWriter(execute_fw); execute_bw.write(cmd + "\n"); execute_bw.close(); execute_fw.close(); String command ="bash " + tempFileName; Process p = Runtime.getRuntime().exec(command); p.waitFor(); br = new BufferedReader(new InputStreamReader(p.getInputStream())); String line; while ((line = br.readLine()) != null) { sb.append(System.lineSeparator()); sb.append(line); } br.close(); br = new BufferedReader(new InputStreamReader(p.getErrorStream())); while ((line = br.readLine()) != null) { sb.append(System.lineSeparator()); sb.append(line); if (line.length() > 0){ execFlag = false; } } br.close(); if (execFlag){ }else { throw new RuntimeException(sb.toString()); } }else { throw new RuntimeException("不支持的操作系統(tǒng)類型"); } } catch (Exception e) { log.error("執(zhí)行失敗",e); }finally { if (br != null){ try { br.close(); } catch (IOException e) { log.error("io異常",e); } } FileUtils.deleteQuietly(new File(tempFileName)); } return sb.toString(); }
三、總結(jié)
實踐是檢驗真理的唯一標(biāo)準(zhǔn),工作生活中一定要多總結(jié),記錄。如果你覺得有用,點個贊吧。收藏一下也是不錯的。
到此這篇關(guān)于Java執(zhí)行shell命令的實現(xiàn)的文章就介紹到這了,更多相關(guān)Java執(zhí)行shell內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
利用JWT如何實現(xiàn)對API的授權(quán)訪問詳解
這篇文章主要給大家介紹了關(guān)于利用JWT如何實現(xiàn)對API的授權(quán)訪問的相關(guān)資料,需要的朋友可以參考下2018-09-09spring中FactoryBean中的getObject()方法實例解析
這篇文章主要介紹了spring中FactoryBean中的getObject()方法實例解析,分享了相關(guān)代碼示例,小編覺得還是挺不錯的,具有一定借鑒價值,需要的朋友可以參考下2018-02-02Springboot下RedisTemplate的兩種序列化方式實例詳解
這篇文章主要介紹了Springboot下RedisTemplate的兩種序列化方式,通過定義一個配置類,自定義RedisTemplate的序列化方式,結(jié)合實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-09-09如何使用java agent修改字節(jié)碼并在springboot啟動時自動生效
本文介紹了JavaAgent的使用方法和在SpringBoot中的應(yīng)用,JavaAgent可以通過修改類的字節(jié)碼,實現(xiàn)對非Spring容器管理對象的AOP處理,演示了如何定義切面邏輯,實現(xiàn)接口mock,感興趣的朋友跟隨小編一起看看吧2024-10-10