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