java调用shell脚本及注意事项
需求:
get方法下載遠程zip包,然后zip包解壓,取出第一級目錄再次進行壓縮獲取新的壓縮zip包。
問題:
如果選擇使用java代碼的IO流操作,在不確定zip包大小的情況下可能會占用很大的內存,所以選擇異步調用shell腳本來實現這個操作;
介紹:
1、通過ProcessBuilder進行調度
//解決腳本沒有執行權限 ProcessBuilder builder = new ProcessBuilder("/bin/chmod", "755", scriptPath); Process process = builder.start(); process.waitFor();
2、直接通過系統的Runtime類執行shell
? ? ? ? Runtime類封裝了運行時的環境。每個 Java 應用程序都有一個 Runtime 類實例,使應用程序能夠與其運行的環境相連接。
一般不能實例化一個Runtime對象,應用程序也不能創建自己的 Runtime 類實例,但可以通過 getRuntime 方法獲取當前Runtime運行時對象的引用。
? ? ? 一旦得到了一個當前的Runtime對象的引用,就可以調用Runtime對象的方法去控制Java虛擬機的狀態和行為。?
遇到的問題:
1、沒權限運行。通過ProcessBuilder來設置文件的權限
//解決腳本沒有執行權限,scriptPath為腳本全路徑 ProcessBuilder builder = new ProcessBuilder("/bin/chmod", "755", scriptPath); Process process = builder.start(); process.waitFor();
2、調用shell腳本提示:No such file or directory
原因:文件格式不正確導致,在windows下編寫的sh文件,文件是DOS格式。使用:set ff=unix 強制將文件轉換為unix格式。
具體操作:
vim模式打開這個shell腳本,查看編碼格式后設置成unix編碼,輸入:set ff?,查看格式是否是fileformat=unix;如果不是,設置成unix
:set ff=unix后保存(:wq)即可。
3、shell腳本輸出太大,程序卡死問題
? ? ? ? Java在執行Runtime.getRuntime().exec(command)之后,Linux會創建一個進程,該進程與JVM進程建立三個管道連接,標準輸入流、標準輸出流、標準錯誤流。
? ? ? ? 當標準輸出流或標準錯誤流非常龐大的時候,會出現調用waitFor方法卡死的bug。真實的環境中,當標準輸出在10000行左右的時候,就會出現卡死的情況。
原因分析:假設linux進程不斷向標準輸出流和標準錯誤流寫數據,而JVM卻不讀取,數據會暫存在linux緩存區,當緩存區存滿之后導致該進程無法繼續寫數據,會僵死,導致java進程會卡死在waitFor()處,永遠無法結束。
解決方式:由于標準輸出和錯誤輸出都會向Linux緩存區寫數據,而腳本如何輸出這兩種流是Java端不能確定的。為了不讓shell腳本的子進程卡死,這兩種輸出需要分別讀取,而且不能互相影響。所以必須新開兩個線程來進行讀取。
new Thread() { ?public void run() { ?BufferedReader br1 = new BufferedReader(new InputStreamReader(is1)); ?try { ?String line1 = null; ?while ((line1 = br1.readLine()) != null) { ?if (line1 != null){} ?} ?} catch (IOException e) { ?e.printStackTrace(); ?} ?finally{ ?try { ?is1.close(); ?} catch (IOException e) { ?e.printStackTrace(); ?} ?} ?} ?}.start(); ?new Thread() { ??public void ?run() { ??BufferedReader br2 = new ?BufferedReader(new ?InputStreamReader(is2)); ??try { ??String line2 = null ; ??while ((line2 = br2.readLine()) != ?null ) { ??if (line2 != null){} ?} ??} catch (IOException e) { ??e.printStackTrace(); ?} ??finally{ ?try { ?is2.close(); ?} catch (IOException e) { ?e.printStackTrace(); ?} ?} ?} ??}.start();下面提供工具類和自己的shell腳本:
工具類:
import org.apache.commons.lang.ArrayUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory;import java.io.*; import java.util.Arrays;public class ShellCommandUtils {/** 日志*/private final static Logger logger = LoggerFactory.getLogger(ShellCommandUtils.class);/*** @Description: 執行shell** @param scriptPath :腳本路徑* @param param 腳本參數* @Return: void* @Date: 2019/3/22?*/public static int execShell(String scriptPath, String... param) {logger.info("調用處理壓縮包的shell腳本,params=" + param.toString());Arrays.stream(param).forEach(item-> logger.info(item));//執行結果int result = 1;try {String[] cmd = new String[]{scriptPath};//為了解決參數中包含空格cmd = (String[]) ArrayUtils.addAll(cmd, param);logger.info("調用處理壓縮包的shell腳本,cmd=" + cmd.toString());Arrays.stream(cmd).forEach(item-> logger.info(item));logger.info("解決腳本沒有執行權限邏輯start");//解決腳本沒有執行權限ProcessBuilder builder = new ProcessBuilder("/bin/chmod", "755", scriptPath);Process process = builder.start();process.waitFor();logger.info("解決腳本沒有執行權限邏輯end");logger.info("開始執行runtime的腳本start");Process ps = Runtime.getRuntime().exec(cmd);logger.info("把緩沖區讀出來打log ?start");//處理InputStream的線程,獲取進程的標準輸入流final InputStream is1 = ps.getInputStream();//獲取進城的錯誤流final InputStream is2 = ps.getErrorStream();//啟動兩個線程,一個線程負責讀標準輸出流,另一個負責讀標準錯誤流new Thread() {public void run() {BufferedReader br1 = new BufferedReader(new InputStreamReader(is1));try {String line1 = null;while ((line1 = br1.readLine()) != null) {if (line1 != null){}}} catch (IOException e) {e.printStackTrace();}finally{try {is1.close();} catch (IOException e) {e.printStackTrace();}}}}.start();new Thread() {public void ?run() {BufferedReader br2 = new ?BufferedReader(new ?InputStreamReader(is2));try {String line2 = null ;while ((line2 = br2.readLine()) != ?null ) {if (line2 != null){}}} catch (IOException e) {e.printStackTrace();}finally{try {is2.close();} catch (IOException e) {e.printStackTrace();}}}}.start();//等待shell腳本結果int execStatus = ps.waitFor();logger.info("執行runtime的腳本end");logger.info("shell腳本執行結果--execStatus ="+execStatus);result = execStatus;logger.info("返回值為result=" + result);} catch (Exception e) {logger.error("調用處理壓縮包的shell出現異常!", e);}return result;}}
shell腳本:
#!/bin/sh
#處理壓縮包
fileName=$1
url=$2
homePath=$3#開始處理數據邏輯
echo fileName=$fileName, url=$url, homePath=$homePath#判斷參數不為空
if [ ?-n "$fileName" ]; ?then?if [ ?-n "$url" ]; then#0.cd到對應目錄cd $homePath#1.調用get方法獲取zip包并下載到本地wget -O $fileName.zip $url#2.解壓zip包unzip $fileName.zip#3.刪除zip包rm -f $fileName.zip#4.進入解壓完的文件夾cd $fileName#5.壓縮當前文件夾下所有文件為指定文件名的zipzip -r ../$fileName.zip ./*#6.刪除之前解壓的文件夾rm -rf $homePath$fileNamefi
fiecho "deal package end"
總結
以上是生活随笔為你收集整理的java调用shell脚本及注意事项的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 计算机网络技术专业的规划,计算机网络技术
- 下一篇: 掌控谈话~让对方说“你说得对